Nullable-Type
1 nullable type
1.1 널 가능성(nullability)
- nullability는 NullPointerException을 피할 수 있게 돕는 코틀린 타입 시스템의 특성이다.
- 코틀린을 비롯한 최신 언어에서 null에대한 접근 방식으로 이 문제를 가능한 실행 시점에서 컴파일 시점으로 옮기고 있다.
- 널이 될수 있는지 여부를 타입 시스템에 추가해 컴파일 러가 이 문제를 컴파일 시 미리 감지해서 실행 시점에 발생할 수 있는 예외의 가능성을 줄인다.
1.2 nullable type
자바
int strLen(String s){
return s.length()
}
- 위 함수는 안전하지 않다.
- null을 인자로 넘기면 NullPointerException이 발생하기 때문
코틀린: 널이 될 수 없는 타입
fun strLen(s:String) = s.length()
- 위 자바 함수를 코틀린으로 작성해보자.
- 이 때 중요한 부분이
함수가 널을 인자로 받을 수 있는가?
이다. - 만약 널이 인자로 들어올 수 없다면 위와 같이 함수를 정의한다.
- s의 타입은
String
인데 이는 항상 s가 String의 인스턴스여야 한다는 뜻이다.- strLen에 null이 될 수 있는 인자를 넘기는 것이 금지되며 null을 넘기려고 하면 컴파일 시 오류가 발생한다.
- 따라서 결코 strLen 함수가 실행 시점에 NullPointerException이 발생하지 않는다.
- 그렇다면 널을 인자로 받게 하려면 어떻게 해야 될까?
- nullable type을 사용하면 된다.
- 아래애서 설명한다.
코틀린: nullable type
fun strLen(s:String?) = s.length()
- 함수가 널을 인자로 받을 수 있게 하려면 위와 같이 타입 이름 뒤에 물음표(?)를 명시해야한다.
- String 타입 뒤에 물음표(?)를 명시했다.
- 물음표를 붙이면 해당 타입의 변수는 프로퍼티에 null 참조를 저장할 수 있다는 뜻이다.
- nullable type의 변수가 있다면 해당 변수에 대해 수행할 수 있는 연산이 제한된다.
- 따라서 위와 같이
s.length()
메서드를 직접 호출할 수 없다.- 위 코드를 컴파일 하면 오류가 발생한다.
- 컴파일 오류를 없애려면 null 체킹이 선행되야 한다.
- 널이 될 수 있는 타입과 null을 비교하교 나면 컴파일러는 그 사실을 기억하고 null이 아님이 확실한 영역에서 해당 값이 널이 될 수 없는 타입의 값처럼 사용할 수 있다.
val x:String? = null
val t:String = x
- 또한 위와 같이 널이 될수 있는 값을 널이 될 수 없는 타입 변수에 대입할 수 없다.
strLen(x)
- 또한 위와 같이 널이 될 수 있는 값을 널이 될 수 없는 타입의 파라미터를 받는 함수에 전달할 수 없다.
2 안전한 호출 연산자 ?.
- 앞서 nullable type은 null 검사를 하지 않으면 연산이 제한된다고 했다.
- null 검사에 사용할 수 있는 도구인 if는 코드가 번잡해지는 일을 피할 수 없다.
- 따라서 코틀린은 null 검사를 간단하게 지원하는 연산자를 지원한다.
- 코틀린에서 제공하는
?.
연산자는 null검사와 메서드 호출을 한 번의 연산으로 수행한다.- 호출하려는 값이 null이 아니리면
?.
는 일반 메서드 호출과 같고 null인 경우 호출은 무시되고 null이 결과 값이 된다. s?.toUpperCase()
는if (s != null) s.toUpperCase() else null
과 같다.
- 호출하려는 값이 null이 아니리면
3 엘비스 연산자 ?:
- 코틀린은 null 대신 디폴트 값을 지정할 때 편리하게 사용할 수 있는 엘비스 연산자를 제공한다.
- 코 틀린에서는 return, throw 등의 연산도 식이기 때문에 엘비스 연산자의 우항으로 사용할 수 있다.
예시
- 엘비스 연산자는 이항 연산자로 좌항 값이 null이 아니면 좌합 값을 결과로 하고 좌항 값이 널이면 우항 값을 결과로 한다.
fun strLenSafe(s: String?): Int = s?.length ?: 0
fun main(args: Array<String>) {
println(strLenSafe("abc"))
println(strLenSafe(null))
}