코틀린에서는 원시 타입과 참조 타입(래퍼 타입, 포인터 변수)을 별도 타입으로 따로 구분하지 않는다.
- 예를 들어 java의 int와 Integer 같이 구분하지 않고, Int 하나로 쓴다.
- 널이 될 수 없는 타입 은 컴파일 시 알아서 원시 타입으로 표현할 수 있는건 원시 타입으로 표현해주고, 메소드를 호출하는 등 래퍼 타입이어야 하는 경우 래퍼 타입으로 변환해준다.
- 널이 될 수 있는 타입 의 경우
null
은 원시 타입에는 들어갈 수 없고, 참조 타입에만 들어갈 수 있으므로 무조건 래퍼 타입으로 컴파일된다.
타입 상한 : 제네릭의 타입 파라미터
T는 T?가 아니어도 널이 될 수 있는 타입이다.
- 제네릭 클래스(T)의 경우 T에 원시 타입을 지정하더라도 내부적으로는 항상 그에 대한 박스 타입을 사용한다. JVM이 타입 인자로 원시 타입을 허용하지 않기 때문.
- 타입 파라미터가 널이 아님을 확실히 해주기 위해서는 반드시 타입 상한(upper bound)을 정해주어야 한다.
1
2
3
4
5
| fun <T> printHashCode(t: T) { // T는 Any?로 추론된다.
println(t?.hashCode()) // 따라서 ?. 를 사용해야 한다.
}
>>> printHashCode(null) // 에러 안남
null
|
1
2
3
4
5
6
| fun <T: Any> printHashCode(t: T) { // T는 Any로 추론된다.
println(t.hashCode())
}
>>> printHashCode(null) // 에러남
error: type parameter bound for T in fun <T : Any> printHashCode(t: T): Unit
is not satisfied: inferred type Nothing? is not a subtype of Any
|
비교 시에는
묵시적 형변환 해주지 않는다. 묵시적 형변환 해주지 않기 때문에 주의해야 한다. 특히 Int
와 Long
을 비교할 때.
1
2
3
4
| >>> val x = 1
>>> val list = listOf(1L, 2L)
>>> x in list
error: type inference failed.
|
연산자는 묵시적 형변환을 지원하도록 오버로딩 되어 있다.
1
2
| val i: Int = 1024
val l: Long = i + 1024L
|
원시 타입 리터럴
1
2
3
4
5
| Long : 10000L
Double : 0.12 1.2e-5
Float : 12.4F
0xDEADBEEF
0b101110
|
Any, Any? : 최상위 타입
- 자바에서는 참조 타입만
Object
를 정점으로 하는 타입 계층에 포함되며, 원시 타입은 계층에 속해있지 않다. - 코틀린에서는
Any
가 원시 타입을 포함한 모든 타입의 조상이다. 그래서 원시 타입을 Any
에 담을 수 있으며 담게되면 Any
는 참조 타입이므로 박싱된다. Any
는 java.lang.Object
에 대응하기는 하지만, toString(), equals(), hashCode()
를 제외한 다른 메소드(wait(), notify()
등)은 kt Any
에서 사용할 수 없다. 사용하려면 java.lang.Object
로 캐스트해야 한다.
Unit 타입 : void
반환 타입 없이 선언한 block body 함수는 자동으로 리턴 타입이 Unit
이다. Unit
은 void
와 달리 타입이다. 따라서 타입 파라미터T
로 쓸 수 있다. Unit
타입에 속하는 값은 딱 하나 있으며, 그 이름도 kt Unit
이다. 리턴 타입이 Unit
인 함수는 묵시적으로 Unit
을 반환한다. 즉, 반환하는 값이 없는게 아니다. 이 것이 Nothing
과의 차이다. * 함수형 프로그래밍에서 Unit
은 ‘단 하나의 인스턴스만 갖는 타입’을 의미한다.
Nothing 타입 : 엘비스 연산자의 우항에 들어가는 함수의 리턴 타입
1
2
3
4
5
6
7
8
9
10
| fun fail(message: String): Nothing {
throw IllegalStateException(message)
}
>>> val company = Company("n", Address("seoul", "kor"))
>>> val address = company.address
>>> println(address.city)
Error: Only safe (?.) or non-null asserted (!!.) calls...
>>> val address = company.address ?: fail("No address")
>>> println(address.city)
seoul
|
1
2
3
4
5
6
7
8
| fun fail(message: String): Unit {
throw IllegalStateException(message)
}
>>> val address = company.address ?: fail("No address")
>>> val i : Int = address
Error:(13, 19) Kotlin: Type mismatch: inferred type is Any but Int was expected
>>> println(address.city)
Error:(14, 21) Kotlin: Unresolved reference: city
|
그래서 엘비스 연산자를 사용할 때 우항에 적는 함수의 리턴 타입은 좌항과 동일(이 경우 Address
)하거나, Nothing
이어야만 한다