Kotiln

코틀린 타입 시스템 - null 관련

블린더르 2019. 7. 19. 16:05

null 가능성(nullability) 은 NullPointerException 오류를 피할 수 있게 돕기 위한 코틀린 타입 시스템의 특성이다.

null이 될 수 있는 타입

변수의 타입 뒤에 ? 를 붙이면 null 참조를 저장할 수 있다는 뜻이다.

var x: String? = null

?. 안전한 호출 연산자

?. 연산자는 null 검사와 메소드 호출을 한번의 연산으로 수행한다.

var allCaps: String? = s?.toUpperCase()

?: 엘비스 연산자

null 대신 사용할 디폴트 값을 지정할 때 사용한다.

var str: String = s ?: "" // s가 null 이면 빈 문자열("")이다

as? 안전한 캐스트

값을 대상 타입으로 변환할 수 없으면 null을 반환한다.

var foo = bar as? String

일반적으로 엘비스 연산자와 함께 사용한다.

var foo = bar as? String ?: ""

!! null 아님 단언 (not-null assertion) 연산자

어떤 값이든 null이 될 수 없는 타입으로 강제로 바꿀 수 있다.
null 에 대해 !! 를 적용하면 NullPointerException 이 발생한다.

let 함수

안전한 호출 연산자 (?.) 와 함께 사용하여 원하는 식의 결과가 null 이 아닐 때 수행하는 로직이 있을 때 사용한다.

foo?.let{ ... it ... }

foo != null 이면 it 은 람다 안에서 null 이 아니다.
foo == null 이면 아무 일도 일어나지 않는다.

나중에 초기화할 프로퍼티

코틀린에서는 클래스 안의 null 이 될 수 없는 프로퍼티를 생성자 안에서 초기화하지 않고 특별한 메소드 안에서 초기화할 수 없다.
lateinit 변경자를 붙여서 프로퍼티를 나중에 초기화할 수 있다.
나중에 초기화하는 프로퍼티는 항상 var 여야 한다 .
초기화하기 전에 프토퍼티에 접근하면 예외가 발생한다.

null이 될 수 있는 타입 확장

null이 될 수 있는 타입에 대한 확장 함수를 정의하면 null 값을 다루는 강력한 도구로 활용할 수 있다.
예로 String? 타입의 수신 객체에 대해 호출할 수 있는 isNullOrEmpty 나 isNullOrBlank 메소드가 있다.

var str: String? = null
if(str.isNullOrBlank()){
    println("str is Null or Blank")
}

안전한 호출 없이도 null이 될 수 있는 수신 객체 타입에 대해 선언된 확장 함수를 호출 가능하다.

isNullOrBlank 함수는 내부적으로 다음과 같이 정의되어 있다.

fun String?.isNullOrBlank() : Boolean = 
    this == null || this.isBlank()

타입 파라미터의 null 가능성

코틀린에서는 함수나 클래스의 모든 타입 파라미터는 기본적으로 null이 될 수 있다.
타입 파라미터 T 를 클래스나 함수 안에서 타입 이름으로 사용하면 이름 끝에 ? 가 없더라도 T 가 null이 될 수 있는 타입이다.

fun <T> printHashCode(t: T){
    println(t?.hashCode())
}

printHashCode(null)

타입 파라미터가 null이 아님을 확실히 하려면 null이 될 수 없는 타입 상한(upper bound) 을 지정해야 한다.
타입 상한을 지정하면 null이 될 수 있는 값을 거부하게 된다.

fun <T: Any> printHashCode (t: T){
    println(t.hashCode())
}

printHashCode(null)    // error
printHashCode(42)        // ok

null 가능성과 자바

자바코틀린
@Nullable StringString?
@NotNull StringString

자바에서 null 가능성 애노테이션이 없는 경우 코틀린의 플랫폼 타입이 된다.

플랫폼 타입

코틀린이 null 관련 정보를 알수 없는 타입을 말한다.
그 타입을 null이 될 수 있는 타입으로 처리해도 되고 null이 될 수 없는 타입으로 처리해도 된다.
null 이 될 수 없는 타입에 null 이 들어가는 경우 NullPointerException이 발생한다.
코틀린에서 플랫폼 타입을 선언할 수는 없으며 자바 코드에서 가져온 타입만 플랫폼 타입이 된다.
하지만 IDE나 컴파일러 오류 메시지에서는 플랫폼 타입을 볼 수 있다.

val i: Int = person.name

> Error: Type mismatch: inferred type is String! but Int was expected

코틀린 컴파일러가 표시한 String! 라는 타입은 자바 코드에서 온 타입니다.
! 표기는 String! 타입의 null 가능성에 대해 아무 정보도 없다는 뜻이다.

상속

코틀린에서 자바 메소드를 오버라이드 할 때 그 메소드의 파마리터와 반환 타입을 null 이 될 수 있는 타입으로 선언할지 null 이 될 수 없는 타입으로 선언할지 결정해야 한다.

구현 메소드를 다른 코틀린 코드가 호출할 수 있으므로 코틀린 컴파일러는 null 이 될 수 없는 타입으로 선언한 모든 파라미터에 대해 null 이 아님을 검사하는 단언문을 만들어준다.
자바 코드가 그 메소드에게 null 값을 넘기면 이 단언문이 발동돼 예외가 발생한다. 파라미터를 메소드 안에서 결코 사용하지 않아도 이런 예외는 피할 수 없다.

참고 도서 : Kotlin in Action (2017)

반응형