코틀린에서는 원시 타입과 참조 타입(래퍼 타입, 포인터 변수)을 별도 타입으로 따로 구분하지 않는다.

  • 예를 들어 java의 int와 Integer 같이 구분하지 않고, Int 하나로 쓴다.
  • 널이 될 수 없는 타입은 컴파일 시 알아서 원시 타입으로 표현할 수 있는건 원시 타입으로 표현해주고, 메소드를 호출하는 등 래퍼 타입이어야 하는 경우 래퍼 타입으로 변환해준다.
  • 널이 될 수 있는 타입의 경우 ``kt null``은 원시 타입에는 들어갈 수 없고, 참조 타입에만 들어갈 수 있으므로 무조건 래퍼 타입으로 컴파일된다.

 

타입 상한 : 제네릭의 타입 파라미터 T는 T?가 아니어도 널이 될 수 있는 타입이다.

  • 제네릭 클래스(T)의 경우 T에 원시 타입을 지정하더라도 내부적으로는 항상 그에 대한 박스 타입을 사용한다. JVM이 타입 인자로 원시 타입을 허용하지 않기 때문.
  • 타입 파라미터가 널이 아님을 확실히 해주기 위해서는 반드시 타입 상한(upper bound)을 정해주어야 한다.

 

```kt

fun <T> printHashCode(t: T) {    // T는 Any?로 추론된다.

    println(t?.hashCode())    // 따라서 ?. 를 사용해야 한다.

}

>>> printHashCode(null)    // 에러 안남

null

```

```kt

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

```

 

비교 시에는 묵시적 형변환 해주지 않는다.

묵시적 형변환 해주지 않기 때문에 주의해야 한다. 특히 ``kt Int``와 ``kt Long``을 비교할 때.
```kt
>>> val x = 1
>>> val list = listOf(1L, 2L)
>>> x in list
error: type inference failed.
```
 

연산자는 묵시적 형변환을 지원하도록 오버로딩 되어 있다.

```kt
val i: Int = 1024
val l: Long = i + 1024L
```
 

원시 타입 리터럴

```kt
Long   : 10000L
Double : 0.12    1.2e-5
Float  : 12.4F
0xDEADBEEF
0b101110
```
 

Any, Any? : 최상위 타입

  • 자바에서는 참조 타입만 ``java Object``를 정점으로 하는 타입 계층에 포함되며, 원시 타입은 계층에 속해있지 않다.
  • 코틀린에서는 ``kt Any``가 원시 타입을 포함한 모든 타입의 조상이다. 그래서 원시 타입을 ``kt Any``에 담을 수 있으며 담게되면 ``kt Any``는 참조 타입이므로 박싱된다.
  • ``kt Any``는 ``kt java.lang.Object``에 대응하기는 하지만, ``kt toString(), equals(), hashCode()``를 제외한 다른 메소드(``java wait(), notify()`` 등)은 ``kt Any``에서 사용할 수 없다. 사용하려면 ``kt java.lang.Object``로 캐스트해야 한다.
 

Unit 타입 : void

반환 타입 없이 선언한 block body 함수는 자동으로 리턴 타입이 ``kt Unit``이다.
``kt Unit``은 ``java void``와 달리 타입이다. 따라서 타입 파라미터`` T``로 쓸 수 있다. ``kt Unit`` 타입에 속하는 값은 딱 하나 있으며, 그 이름도 ``kt Unit``이다.
리턴 타입이 ``kt Unit``인 함수는 묵시적으로 ``kt Unit``을 반환한다. 
즉, 반환하는 값이 없는게 아니다. 이 것이 ``kt Nothing``과의 차이다.
* 함수형 프로그래밍에서 ``kt Unit``은 '단 하나의 인스턴스만 갖는 타입'을 의미한다.

 

Nothing 타입 : 엘비스 연산자의 우항에 들어가는 함수의 리턴 타입

```kt
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
```
  • ``kt ?.``가 아니라 그냥 `` .``으로 사용할 수 있는 것은 엘비스 연산자 덕분이다.
    • ``kt Nothing`` 타입은 "이 함수는 항상 실패하는 함수"라는 정보를 컴파일러에게 알려주기는 하지만, ?: 뒤에 다른 것을 적어도 그냥 .으로 연결 가능하다.
  • 그럼에도 ``kt Nothing`` 타입을 사용해야 하는 이유는,
    • 타입을 생략해서 함수의 리턴 타입이 ``kt Unit``이 되면, 다른 아무거나 타입을 리턴 타입으로 지정하는 경우 엘비스 연산자의 우항이 절대 실행되지 않더라도 스마트 캐스트가 동작해 ``kt address``의 타입이 ``kt Any``가 되어 버린다.
    • 따라서 별도의 캐스팅이 필요하거나, null 아님을 다시 체크해줘야 한다.
 
```kt
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
```
 
그래서 엘비스 연산자를 사용할 때 우항에 적는 함수의 리턴 타입은 좌항과 동일(이 경우 ``kt Address``)하거나, ``kt Nothing``이어야만 한다