더북(TheBook)

코틀린에서 String? 같은 타입은 널이 될 수 있는 타입(nullable type)이라고 불린다. 타입 시스템 용어에서 모든 널이 될 수 있는 타입은 원래 타입(?가 붙지 않은 타입)의 상위 타입이며, 원래 타입에 속하는 모든 값으로 이뤄진 집합을 null로 확장한 집합이 값의 집합이 된다. 이 말은 특히 널이 될 수 있는 타입의 변수에 항상 널이 될 수 없는 타입의 값을 대입할 수 있다는 뜻이다. 하지만 물론 반대로 널이 될 수 없는 타입의 변수에 널이 될 수 있는 타입의 값을 대입할 수는 없다.

fun main() {
  println(isBooleanString(null)) // Ok
  val s: String? = "abc"         // Ok
  // error: type mismatch: inferred type is String? but String was expected 
  val ss: String = s
}

앞 예제의 마지막 대입문은 잘못된 문장이다. 변수 s가 런타임에 null 값을 저장할 수 없지만, 우리가 s의 타입을 널이 될 수 있는 타입으로 지정했기 때문에 컴파일러는 이 정적인 타입 정보만 사용해 보수적으로 판단할 수밖에 없다.

런타임에 널이 될 수 없는 값은 실제로 널이 될 수 있는 값과 차이가 없다. 둘 사이 구분은 컴파일 수준에서만 존재한다. 코틀린 컴파일러는 널이 될 수 없는 값을 표현하기 위해 어떤 래퍼(예를 들어 자바 8의 Optional 클래스 같은)도 사용하지 않는다. 따라서 런타임에는 어떠한 부가 비용도 들지 않는다.

IntBoolean 같은 원시 타입도 널이 될 수 있는 타입이 존재한다. 하지만 원시 타입의 널이 될 수 있는 타입은 항상 박싱한 값만 표현한다는 점을 명심하라.

fun main() {
  val n: Int = 1   // 원시 타입의 값
  val x: Int? = 1  // 박싱한 타입의 값을 참조
}
신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.