
DataClass는 매개체의 데이터를 관리하는 용도로 사용하는 클래스
abstract, open, sealed, inner 클래스로 정의할 수 없다.
반드시 주 생성자를 가지고 있어야 한다.
DataClass는 개발자의 개발 편리성을 위해 몇가지 메서드가 자동으로 구현된다
equals : 객체가 가지고 있는 변수를 모두 비교(주생성자에 정의되어있는 변수 한정)
hashCode : 객체를 구분하기 위한 고유한 정수값
copy : 객체를 복제하는 메서드
toStrihng : 객체가 가지고 있는 변수의 값을 출력
componentN : 객체 분해
fun main() {
// 일반 클래스의 객체를 생성한다.
val obj1 = TestClass1(100,200)
val obj2 = TestClass1(100,200)
// 데이터 클래스의 객체를 생성한다.
val obj3 = TestClass2(100,200)
val obj4 = TestClass2(100,200)
// 일반 클래스를 통해 생성한 객체를 비교한다.
// 코틀린에서 객체간에 == 연산자를 사용하면 equals 메서드를 호출하여 그 결과를 반환한다.
// 만약 클래스에 equals 메서드를 구현하지 않으면 변수에 저장되어 있는 값을 비교한다.
// 즉 변수가 가지고 있는 객체의 주소값이 같은지를 비교한다.
if(obj1 == obj2){
println("obj1과 obj2는 같은 객체입니다.")
}else{
println("obj1과 obj2는 다른 객체입니다.")
}
// 데이터 클래스를 통해 생성한 객체를 비교한다.
// 데이터 클래스는 equals 메서드가 구현되어 있으며 객체가 가지고 있는 프로퍼티의 값이
// 같은지를 비교해준다.
if(obj3 == obj4){
println("obj3과 obj4는 주 생성자에 정의된 프로퍼티의 값이 같습니다.")
}else{
println("obj3과 obj4는 주 생성자에 정의된 프로퍼티의 값이 다릅니다.")
}
// copy : 객체를 복제하여 새로운 객체를 생성한다.
// 같은 클래스를 통해 객체를 생성하고 주 생성자에 정의되어 있는 프로퍼티의 값을 추출하여
// 새롭게 생성된 객체에 저장해준다.
val obj5 = obj3.copy()
println("obj5 : ${obj5.a1}")
println("obj5 : ${obj5.a2}")
println("obj3 : ${obj3.a1}")
println("obj3 : ${obj3.a2}")
obj5.a1 = 1000
println("obj5.a1 : ${obj5.a1}")
println("obj3.a1 : ${obj3.a1}")
// toString : 주생성자에 정의되어 있는 프로프티의 값을 확인할 수 있는 형태로 구현되어 있다.
println("obj1 : $obj1")
println("obj3 : $obj3")
// componentN : 주 생성자에 정의되어 있는 프로퍼티의 개수 만큼 생성된다.
// component1은 첫 번째 프로퍼티를 의미하고 component2는 두 번째 프로퍼티를 의미한다.
// 각 프로퍼티가 가지고 있는 값을 반환받을 수 있다.
val num1 = obj3.component1()
val num2 = obj3.component2()
println("num1 : $num1")
println("num2 : $num2")
// 첫 번째 변수인 num3 에는 component1을 호출하여 반환하는 값이 저장되고
// 두 번째 변수인 num4 에는 component2을 호출하여 반환하는 값이 저장된다.
val (num3, num4) = obj3
println("num3 : $num3")
println("num4 : $num4")
}
// 일반 클래스
class TestClass1(var a1:Int, var a2:Int)
// 데이터 클래스
data class TestClass2(var a1:Int, var a2:Int)
obj1과 obj2는 다른 객체입니다.
obj3과 obj4는 주 생성자에 정의된 프로퍼티의 값이 같습니다.
obj5 : 100
obj5 : 200
obj3 : 100
obj3 : 200
obj5.a1 : 1000
obj3.a1 : 100
obj1 : TestClass1@234c8c9b
obj3 : TestClass2(a1=100, a2=200)
num1 : 100
num2 : 200
class TestClass1<T>{
fun testMethod1(a1:T){
println("a1 : $a1")
}
}
fun main() {
// 제네릭 타입은 객체를 생성할 때 타입을 결정해줘야 한다.
// T 타입으로 정의된 변수의 타입이 Int 로 결정된다.
val t1 = TestClass1<Int>()
t1.testMethod1(100)
val t2 = TestClass1<String>()
t2.testMethod1("안녕하세요")
}
a1 : 100
a1 : 안녕하세요
class TestClass2<A, B, C, D>(var a1:A, var a2:B){
fun testMethod3(a3:C, a4:D){
println("a1 : $a1")
println("a2 : $a2")
println("a3 : $a3")
println("a4 : $a4")
}
}
fun main() {
val t3 = TestClass2<Int, Double, Boolean, String>(100,11.11)
t3.testMethod3(true, "문자열1")
val t4 = TestClass2<Double, String, Boolean, Int>(22.22,"문자열2")
t4.testMethod3(false, 200)
}
a1 : 100
a2 : 11.11
a3 : true
a4 : 문자열1
a1 : 22.22
a2 : 문자열2
a3 : false
a4 : 200
fun main() {
val t5:TestClass3<SubClass1> = TestClass3<SubClass1>()
// val t6:TestClass3<SuperClass1> = TestClass3<SubClass1>() // 에러
// val t6:TestClass3<SubClass2> = TestClass3<SubClass1>() // 에러
}
open class SuperClass1
open class SubClass1 : SuperClass1()
class SubClass2 : SubClass1()
// 불변성(제네릭에 키워드를 붙히지 않는다)
class TestClass3<T>
fun main() {
val t8:TestClass4<SubClass1> = TestClass4<SubClass1>()
val t9:TestClass4<SuperClass1> = TestClass4<SubClass1>()
// val t10:TestClass4<SubClass2> = TestClass4<SubClass1>() // 에러
}
open class SuperClass1
open class SubClass1 : SuperClass1()
class SubClass2 : SubClass1()
// 공변성
class TestClass4<out A>()
fun main() {
val t11:TestClass5<SubClass1> = TestClass5<SubClass1>()
// val t12:TestClass5<SuperClass1> = TestClass5<SubClass1>() // 에러
val t13:TestClass5<SubClass2> = TestClass5<SubClass1>()
}
open class SuperClass1
open class SubClass1 : SuperClass1()
class SubClass2 : SubClass1()
// 반 공변성
class TestClass5<in A>()
fun main() {
// 내부 클래스의 객체를 생성하기 위해서는 외부 클래스의 객체가 필요하다.
val obj1 = Outer1()
val obj2 = obj1.Inner1()
}
// 일반 중첩 클래스
// 외부 클래스
class Outer1{
// 내부 클래스
inner class Inner1{
}
}
fun main() {
// 내부 클래스의 객체를 생성하기 위해서는 외부 클래스의 객체가 필요하다.
val obj1 = Outer1()
obj1.Inner1().innerValue1
}
// 일반 중첩 클래스
// 외부 클래스
class Outer1{
//프로퍼티
var outerValue1 = 100
// 메서드
fun outerMethod1(){
println("Outer1의 outerMethod1")
// 내부 클래스가 가지고 있는 요소를 사용한다.
// 외부 클래스의 객체를 생성했다고 해서 내부 클래스의 객체가 생성되는 것이 아니므로
// 외부 클래스에서 내부 클래스의 요소는 사용할 수 없다.
// println("innerValue : $innerValue1") // 에러
// innerMethod1() // 에러
}
// 내부 클래스
inner class Inner1{
//프로퍼티
var innerValue1 = 200
// 메서드
fun innerMethod1(){
println("Inner1의 innerMethod1")
// 내부 클래스의 객체를 생성하려면 외부 클래스를 통해 만든 객체가 필요하다.
// 따라서 내부 클래스를 가지고 만든 객체 입장에서는 외부 클래스의 객체가 생성되어
// 있다는 것을 보장받을 수 있다. 이에 외부 클래스에 정의된 모든 멤버를 사용할 수 있다.
println("outerValue1 : $outerValue1")
outerMethod1()
}
}
}
// 인터페이스나 추상 클래스를 통해 객체를 생성할 수 없다.
// 인터페이스를 구현하여 메서드를 구현한 클래스를 정의하거나 추상클래스를 상속받아 메서드를 구현한
// 클래스를 만들어야 객체를 생성할 수 있다.
// 만약 이러한 클래스를 통해 생성하는 객체가 하나뿐이라면 익명 중첩클래스를 사용하면 편하게 작업할 수 있다.
interface Inter1{
fun interMethod1()
fun interMethod2()
}
class TestClass1:Inter1{
override fun interMethod1() {
println("TestClass1의 interMethod1")
}
override fun interMethod2() {
println("TestClass2의 interMethod2")
}
}
fun main() {
// 다른 위치, 파일, 패키지, 모듈에 클래스를 작성했다면 그곳에서 클래스를 작성하고
// 여기서 객체 생성
val obj3 = TestClass1()
obj3.interMethod1()
obj3.interMethod2()
// 만약 익명 중첩클래스를 사용한다면
// 클래스 작성과 메서드의 오버라이딩과 객체 생성을 같은 곳에서 모두 작업한다.
// 단, 이름이 없는 1회용 클래스이기 때문에 이 클래스를 더 이상 사용할 수 없다.
// object : 인터페이스나 추상클래스
val obj4 = object : Inter1{
override fun interMethod1() {
println("익명 중첩 클래스의 interMethod1")
}
override fun interMethod2() {
println("익명 중첩 클래스의 interMethod2")
}
}
obj4.interMethod1()
obj4.interMethod2()
}
익명 중첩 클래스의 interMethod1
익명 중첩 클래스의 interMethod2
fun main() {
// null 을 허용하지 않는 프로퍼티
// var a1:TestClass1 = null
// null 을 허용하는 프로퍼티
// null 을 허용하는 프로퍼티를 선언할 때는 타입을 반드시 작성해줘야 한다.
var a2:TestClass1? = null
// 변수에 null 값이 들어있는 상태에서 메서드를 호출
// a2.test1Method() //에러
// a2!!.test1Method() // NullPointerException 에러 발생
}
class TestClass1{
fun test1Method(){
println("TestClass1의 test1Method")
}
// 값을 반환하는 메서드
fun test2Method():Int{
return 100
}
}
fun main() {
// null을 허용하지 않는 타입 변수에 null을 허용하지 않는 타입의 변수의 값을 넣어준다.
val t4:TestClass1 = t1
// null을 허용하는 타입의 변수 뒤에 !!를 붙여서 null을 허용하지 않는 타입으로 변경
// 이 때 null 값이 아닌 객체의 주소값이 들어 있으므로 정상 변환된다.
val t5:TestClass1 = t2!!
println("t4 : $t4")
println("t5 : $t5")
// null을 허용하는 타입의 변수 뒤에 !!를 붙여서 null을 허용하는 타입의 값으로 변경
// 이 때 null 값이 들어있으므로 변환 때 오류가 발생한다.
// 변수에 null아닌 객체의 주소값이 들어 있다는 것을 100% 보장할 수 있을 경우에만 사용한다.
// val t6:TestClass1 = t3!! // 에러
// println("t6 : $t6")
}
class TestClass1{
fun test1Method(){
println("TestClass1의 test1Method")
}
}
변수 ? : 기본값 : null 값을 허용하는 변수를 사용할 때 null이 들어가 있을 경우 객체 대신에 기본값으로 설정되어 있는 것을 전달해 준다.fun main() {
var t7:TestClass1? = null
var t8:TestClass1? = TestClass1()
var t9:TestClass1 = t7 ?: TestClass1()
var t10:TestClass1 = t8 ?: TestClass1()
println("t7 : ${t7}, t9 : $t9")
println("t8 : ${t8}, t10 : $t10")
}
class TestClass1{
var testValue1 = 100
fun test1Method(){
println("TestClass1의 test1Method")
}
// 값을 반환하는 메서드
fun test2Method():Int{
return 100
}
}
t7 : null, t9 : TestClass1@e87f4b7c
t8 : TestClass1@cc814459, t10 : TestClass1@cc814459
fun main() {
val t11:TestClass1? = null
val t12:TestClass1? = TestClass1()
// 프로퍼티
// 객체를 가지고 있는 변수에 null이 들어가 있으면 프로퍼티 접근이 중단되고
// null을 반환한다.
println("t11.testValue1 : ${t11?.testValue1}")
println("t12.testValue1 : ${t12?.testValue1}")
// 메서드
// null이 들어가 있으면 메서드 호출이 취소된다.
t11?.test1Method()
t12?.test1Method()
}
class TestClass1{
var testValue1 = 100
fun test1Method(){
println("TestClass1의 test1Method")
}
// 값을 반환하는 메서드
fun test2Method():Int{
return 100
}
}
t11.testValue1 : null
t12.testValue1 : 100
TestClass1의 test1Method
fun main() {
// 객체 생성
// 부모 클래스형 타입에 담는다.
val obj1:SuperClass1 = SubClass1()
// 구현한 인터페이스형 타입에 담는다.
val obj2:Inter1 = SubClass2()
// 객체의 주소값을 가지고 있는 변수의 타입이 부모클래스 타입이므로
// 부모가 가지고 있는 메서드이거나 자식에서 overriding한 메서드만 호출할 수 있다.
obj1.superMethod1()
// obj1.subMethod1() // 에러
// 객체의 주소값을 가지고 있는 변수의 타입이 구현한 인터페이스 타입이므로
// overriding한 메서드만 호출할 수 있다.
obj2.interMethod1()
// obj2.subMethod2() // 에러
}
open class SuperClass1{
fun superMethod1(){
println("SuperClass1의 superMethod1 입니다.")
}
}
interface Inter1{
fun interMethod1()
}
class SubClass1 : SuperClass1(){
fun subMethod1(){
println("SubClass1의 subMethod1 입니다")
}
}
class SubClass2 : Inter1{
fun subMethod2(){
println("SubClass2의 subMethod2 입니다")
}
override fun interMethod1() {
println("SubClass2의 interMethod1 입니다")
}
}
fun main() {
val obj1:SuperClass1 = SubClass1()
val obj2:Inter1 = SubClass2()
// as : 객체의 주소값을 가지고 있는 변수의 타입을 다른 클래스 타입으로 변환한다.
val obj3 = obj1 as SubClass1
obj1.superMethod1()
obj1.subMethod1()
obj3.superMethod1()
obj3.subMethod1()
// 위의 클래스는 obj1에 SubClass1을 가지고 만든 객체의 주소값이 담겨져 있기 때문에 가능한 것이다.
// 만약 다른 클래스타입으로 변환을 할 경우 오류가 발생한다.
// val obj4 = obj1 as String // 에러
}
open class SuperClass1{
fun superMethod1(){
println("SuperClass1의 superMethod1 입니다.")
}
}
interface Inter1{
fun interMethod1()
}
class SubClass1 : SuperClass1(){
fun subMethod1(){
println("SubClass1의 subMethod1 입니다")
}
}
class SubClass2 : Inter1{
fun subMethod2(){
println("SubClass2의 subMethod2 입니다")
}
override fun interMethod1() {
println("SubClass2의 interMethod1 입니다")
}
}
SuperClass1의 superMethod1 입니다.
SubClass1의 subMethod1 입니다
SuperClass1의 superMethod1 입니다.
SubClass1의 subMethod1 입니다
fun main() {
// is : 객체를 생성했을 때 사용했던 클래스가 무엇인지 확인할 수 있다.
// 또한, 객체를 생성했을 때 사용했던 클래스의 부모클래스도 확인할 수 있다.
// 의미는 객체에 지정한 클래스 부분이 있는지를 확인하는 것이다.
val obj5 = SuperClass1()
var chk1 = obj5 is SubClass1
var chk2 = obj5 is SuperClass1
var chk3 = obj5 is Inter1
println("chk1 : $chk1")
println("chk2 : $chk2")
println("chk3 : $chk3")
}
open class SuperClass1{
fun superMethod1(){
println("SuperClass1의 superMethod1 입니다.")
}
}
interface Inter1{
fun interMethod1()
}
class SubClass1 : SuperClass1(){
fun subMethod1(){
println("SubClass1의 subMethod1 입니다")
}
}
class SubClass2 : Inter1{
fun subMethod2(){
println("SubClass2의 subMethod2 입니다")
}
override fun interMethod1() {
println("SubClass2의 interMethod1 입니다")
}
}
chk1 : false
chk2 : true
chk3 : false
fun main() {
// 스마트 캐스팅 : 특정 조건을 만족하면 자동으로 형변환이 이루어지는 것을 의미한다.
val obj6:SuperClass1 = SubClass1()
// SubClass1부분이 있는지 검사한다.
// 아래와 같이 객체에 특정 클래스 부분이 있는지를 검사하는 코드는
// 해당 타입으로 형변환 한 다음에 메서드나 프로퍼티를 호출하려고 하는 경우가 많다.
// 이제 코틀린은 검사 대상 타입으로 변환까지 해준다.
// obj6.subMethod1() // 에러
if(obj6 is SubClass1){
obj6.subMethod1()
}
// 위의 if문이 끝나면 원래의 타입으로 돌아온다.
// obj6.subMethod1() // 에러
}
open class SuperClass1{
fun superMethod1(){
println("SuperClass1의 superMethod1 입니다.")
}
}
interface Inter1{
fun interMethod1()
}
class SubClass1 : SuperClass1(){
fun subMethod1(){
println("SubClass1의 subMethod1 입니다")
}
}
class SubClass2 : Inter1{
fun subMethod2(){
println("SubClass2의 subMethod2 입니다")
}
override fun interMethod1() {
println("SubClass2의 interMethod1 입니다")
}
}
SuperClass1의 superMethod1 입니다.
fun main() {
val obj1:SuperClass1 = SubClass1()
val obj2:Inter1 = SubClass2()
// 매개변수가 Any인 함수 호출
// Any는 모든 클래스의 최상위 부모이고 코틀린에서 기본 자료형(Int, Float..)도 사실 클래스임
anyFunction(obj1)
anyFunction(obj2)
anyFunction(100)
anyFunction("안녕하세요")
}
open class SuperClass1{
fun superMethod1(){
println("SuperClass1의 superMethod1 입니다.")
}
}
interface Inter1{
fun interMethod1()
}
class SubClass1 : SuperClass1(){
fun subMethod1(){
println("SubClass1의 subMethod1 입니다")
}
}
class SubClass2 : Inter1{
fun subMethod2(){
println("SubClass2의 subMethod2 입니다")
}
override fun interMethod1() {
println("SubClass2의 interMethod1 입니다")
}
}
// 매개변수로 Any 타입을 가지고 있는 함수
fun anyFunction(obj:Any){
// is를 통해 클래스 타입을 확인하고
// 스마트 캐스팅을 이용해 메서드를 호출할 수 있다.
if(obj is SubClass1){
obj.subMethod1()
}
if(obj is SubClass2){
obj.subMethod2()
}
if(obj is Int){
println("정수 입니다")
}
if(obj is String){
println("문자열 입니다")
}
}
SubClass1의 subMethod1 입니다
SubClass2의 subMethod2 입니다
정수 입니다
문자열 입니다
fun main(){
val str1 = "100"
val number1 = str1.toInt()
if(number1 is Int){
println("정수로 변환되었습니다.")
}
val str2 = number1.toString()
if(str2 is String){
println("문자열로 변환되었습니다.")
}
}
정수로 변환되었습니다.
문자열로 변환되었습니다.
// null을 허용하는 변수에 null이 아닌 객체의 주소값이 들어가 있음을 확신 시켜주면
// 스마트 캐스팅이 발생하여 null을 허용하지 않는 타입으로 변환해준다.
// 강사님은 ?연산자 등 대신 이 방법을 권장함
fun main() {
val t1:TestClass1? = TestClass1()
val t2:TestClass1? = null
// !! : null을 허용하는 변수의 값을 null을 허용하지 않는 변수에 담을 때 사용한다
// 만약 null이 들어가 있으면 오류가 발생한다.
val t3 = t1!!
// val t4 = t2!! // 에러
// ?: : null일 경우 새로운 객체를 생성하고자 할 때 사용한다.
val t5 = t1 ?: TestClass1()
val t6 = t2 ?: TestClass1()
// ? : 객체가 가지고 있는 프로퍼티나 메서드를 사용하고자 할 때 사용한다.
// 프로퍼티인 경우 null이 반환되고 메서드인 경우 호출이 취소된다.
t1?.testMethod1()
t2?.testMethod1()
// if문으로 null 여부를 검사해주면 null을 허용하지 않는 타입으로 스마트 캐스팅이 발생한다.
if(t1 != null){
t1.testMethod1()
}
if(t1 is TestClass1){
t1.testMethod1()
}
}
class TestClass1{
fun testMethod1(){
println("TestClass1의 testMethod1 입니다")
}
}
fun main() {
printDirection(Direction2.SOUTH)
}
// 정적 멤버
class Direction2{
companion object{
val NORTH = 0
val SOUTH = 1
val WEST = 2
val EAST = 3
}
}
// 매개변수로 들어오는 값으로 분기하여 출력하는 함수를 만든다
// 정적 멤버 사용
fun printDirection(dir:Int){
when(dir){
Direction2.WEST -> println("서쪽입니다")
Direction2.SOUTH -> println("남쪽입니다")
Direction2.NORTH -> println("북쪽입니다")
Direction2.EAST -> println("동쪽입니다")
// 정적 멤버로 정의된 변수들은 서로간에 어떠한 관계가 있는 것이 아니기 때문에
// 몇가지 경우를 빼더라도 문제가 발생하지 않는다.
}
}
남쪽입니다
fun main() {
printDirection2(Direction.EAST)
}
// 열거형 정의
enum class Direction{
NORTH, SOUTH, WEST, EAST
}
// 매개변수로 들어오는 값으로 분기하여 출력하는 함수를 만든다
// 열거형 사용
fun printDirection2(dir:Direction){
when(dir){
Direction.EAST -> println("동쪽입니다")
Direction.SOUTH -> println("남쪽입니다")
Direction.NORTH -> println("북쪽입니다")
Direction.WEST -> println("서쪽입니다")
// 열거형에 정의된 값들은 하나의 열거형 안에 정의된 값이라는 관계를 갖기 때문에
// 모든 경우를 다 적어주거나 빠진 경우가 있을 시 빠진 것에 대한 처리를 할 수 있도록 else 문을 넣어줘야 한다.
}
}
동쪽입니다
// 매개변수로 들어오는 값에 따라 값을 반환하는 함수
// 정적멤버 사용
fun getValue1(a1:Int) = when(a1){
Direction2.WEST -> "서쪽"
Direction2.EAST -> "동쪽"
Direction2.SOUTH -> "남쪽"
Direction2.NORTH -> "북쪽"
// 각각이 독립적인 변수들이기 때문에 더 있을 수도 있다고 컴퓨터가 판단하기 때문에
// 반드시 else를 넣어줘야 한다.
else -> "아무 방향"
}
fun main() {
val r1 = getValue1(Direction2.SOUTH)
println("r1 : $r1")
}
r1 : 남쪽
// 매개변수로 들어오는 값에 따라 값을 반환하는 함수
// 열거형 사용
// 하나의 열거형 안에 정의 되어 있는 모든 경우에 대해 작성해주면
// else는 작성하지 않아도 된다. 만약 빠진게 있다면 else를 넣어준다.
fun getValue2(a1:Direction) = when(a1){
Direction.EAST -> "동쪽"
Direction.WEST -> "서쪽"
Direction.SOUTH -> "남쪽"
Direction.NORTH -> "북쪽"
}
fun main() {
val r2 = getValue2(Direction.EAST)
println("r2 : $r2")
}
r2 : 동쪽
enum class Number(val num:Int, val str:String){
ONE(1, "일"),
TWO(2, "이"),
THREE(3, "삼")
}
fun printValue3(v1:Number){
when(v1){
Number.ONE -> println("1 입니다")
Number.TWO -> println("2 입니다")
Number.THREE -> println("3 입니다")
}
when(v1.num){
1 -> println("1 입니다")
2 -> println("2 입니다")
3 -> println("3 입니다")
}
when(v1.str){
"일" -> println("1 입니다")
"이" -> println("2 입니다")
"삼" -> println("3 입니다")
}
}
fun main(){
printValue3(Number.TWO)
}
2 입니다
2 입니다
2 입니다
// Sealed 클래스 : 특정 클래스를 상속받은 클래스들을 열거형 처럼 모아 관리하는 개념
// Sealed 클래스가 부모클래스가 되고 Sealed 클래스가 관리하는 모든 클래스는 Sealed 클래스를
// 상속받은 자식 클래스에 해당한다.
fun main() {
// 객체를 생성한다.
val obj1 = NumberClass.OneClass(100,200)
val obj2 = NumberClass.TwoClass(300)
val obj3 = NumberClass.ThreeClass(100,11.11)
obj1.numberMethod1()
obj2.numberMethod1()
obj3.numberMethod1()
}
// 열거형
enum class Number1(var num:Int, var str:String){
ONE(1,"일"), TWO(2,"이"), THREE(3,"삼")
}
// Sealed 클래스는 자기 자신을 상속받은 클래스들을 모아 관리하는 개념
sealed class NumberClass{
open fun numberMethod1(){
println("NumberClass의 numberMethod1")
}
// 관리할 클래스
// 모든 클래스는 Sealed 클래스를 상속받는다.
class OneClass(var a1:Int, var a2:Int) : NumberClass()
class TwoClass(var a1:Int) : NumberClass(){
override fun numberMethod1() {
println("TwoClass의 numberMethod1")
}
}
class ThreeClass(var a1:Int, var a2:Double) : NumberClass()
}
NumberClass의 numberMethod1
TwoClass의 numberMethod1
NumberClass의 numberMethod1
// 매개변수로 들어오는 객체의 클래스 타입에 따라 분기해 처리한다.
// 함수의 매개변수에 Sealed 클래스 타입을 넣어준다.
fun checkNumber(t1:NumberClass){
// when으로 분기할 때 is를 통해 어떤 클래스를 통해 만든 객체인지 검사한다.
// 이 때 스마트 캐스팅도 이루어진다.
when(t1){
is NumberClass.OneClass -> {
println("OneClass입니다")
println(t1.a1)
println(t1.a2)
t1.numberMethod1()
}
is NumberClass.TwoClass -> {
println("TwoClass입니다")
println(t1.a1)
t1.numberMethod1()
}
is NumberClass.ThreeClass -> {
println("ThreeClass입니다")
println(t1.a1)
println(t1.a2)
t1.numberMethod1()
} // ThreeClass까지 모두 작성해야 함
}
}
fun main() {
val obj1 = NumberClass.OneClass(100,200)
val obj2 = NumberClass.TwoClass(300)
val obj3 = NumberClass.ThreeClass(100,11.11)
checkNumber(obj1)
checkNumber(obj2)
checkNumber(obj3)
}
OneClass입니다
100
200
NumberClass의 numberMethod1
TwoClass입니다
300
TwoClass의 numberMethod1
ThreeClass입니다
100
11.11
NumberClass의 numberMethod1
import kotlin.reflect.KClass
// Reflection : 프로그램 실행 중에 객체에 대한 다양한 정보를 파악할 수 있다.
class TestClass
fun main() {
// 클래스 타입
// KClass<클래스타입> : 지정된 클래스의 타입을 파악한다(코틀린 클래스)
val a1:KClass<String> = String::class
println("String의 코틀린에서의 타입 : $a1")
// Class<클래스타입> : 지정된 클래스의 타입을 파악한다(자바 클래스)
val a2:Class<String> = String::class.java
println("String의 자바에서의 타입 : $a2")
// 변수를 통해 접근할 수 있는 객체의 클래스 타입을 파악한다.
val str1 = "안녕하세요"
val a3:KClass<out String> = str1::class
println("str1을 통해 접근할 수 있는 객체의 클래스 타입(코틀린) : $a3")
val a4:Class<out String> = str1::class.java
println("str1을 통해 접근할 수 있는 객체의 클래스 타입(자바) : $a4")
val test1 = TestClass()
val a7 = test1::class
val a8 = test1::class.java
println("test1의 클래스 타입(코틀린) : $a7")
println("test1의 클래스 타입(자바) : $a8")
// 클래스 정보 분석
println("추상 클래스 인가 : ${test1::class.isAbstract}")
println("Companion 클래스 인가 : ${test1::class.isCompanion}")
println("Data 클래스 인가 : ${test1::class.isData}")
println("Final 클래스 인가 : ${test1::class.isFinal}")
println("open 클래스 인가 : ${test1::class.isOpen}")
println("중첩 클래스 인가 : ${test1::class.isInner}")
println("Sealed 클래스 인가 : ${test1::class.isSealed}")
}
String의 코틀린에서의 타입 : class java.lang.String (Kotlin reflection is not available)
String의 자바에서의 타입 : class java.lang.String
str1을 통해 접근할 수 있는 객체의 클래스 타입(코틀린) : class java.lang.String (Kotlin reflection is not available)
str1을 통해 접근할 수 있는 객체의 클래스 타입(자바) : class java.lang.String
test1의 클래스 타입(코틀린) : class TestClass (Kotlin reflection is not available)
test1의 클래스 타입(자바) : class TestClass
※ 출처 : 멋쟁이사자 앱스쿨 2기, 소프트캠퍼스