Kotlin으로 코드를 작성하다보면 보게되는 연산자들이 있습니다.
?. , ?: , !! 모두 Null 타입을 처리하기위해 사용하는 연산자들입니다.
Java와 비교하면서 천천히 알아보겠습니다.
In Java
private void doSomething(String str) {
System.out.println(str.length());
}
위의 코드는 안전한 코드일까요?
.
.
.
아닙니다.
str 이 null 일 가능성이 있기 때문입니다. null에 대해 length()를 호출하면 NullPointerException이 발생하죠.
private void doSomething(String str) {
if (str != null) {
System.out.println(str.length());
}
}
이렇게 써야 안전한 코드라고 할 수 있겠습니다.
In Kotlin
Kotlin은 어떨까요?
private fun doSomething(str: String) {
println(str.length)
}
위의 코드는 안전한 코드일까요?
.
.
.
맞습니다.
왜일까요?
Kotlin은 null 이 가능한 타입과 불가능한 타입을 확실히 구분합니다.
String 타입은 절대 null이 될 수 없는 타입입니다.String? 타입이 nullable 타입입니다.
따라서 str은 String 타입이므로 null check를 안해도 됩니다.
private fun doSomething(str: String?) {
if (str != null) {
println(str.length)
}
}
String? 같은 nullable 타입일 때만 null check를 해주면 됩니다.
하지만 코틀린에서 제공해주는 ?. , ?: , !! 를 사용하면 더 섹시하게 처리할 수 있습니다.
?. Safe call operator
private fun doSomething(str: String?) {
val length = str?.length
println(length)
// println(str?.length)
}
str?.length 를 length 변수에 할당해서 사용했습니다.?.는 safe call operator라고 합니다.
str.length 를 호출 하면 str이 null일 수 도 있기 때문에 NPE가 발생할 확률이 있습니다.
(Kotlin에선 컴파일 오류)
애초에 NPE가 왜 발생할까요?
우선 length() 는 String 클래스 안에 정의된 함수(메소드)입니다.
str변수에 String 인스턴스가 할당돼있지 않은 상태에서 String 클래스의 함수를 호출하기 때문입니다.null.length() 를 호출하는 꼴입니다. null에는 당연히 length() 라는 함수가 없습니다.
그래서 NPE가 발생합니다.
safe call operator를 사용해서 str?.length 호출하게되면 str이 null일 경우 뒤의 호출을 무시하고 식의 전체 결과가 null로 반환됩니다.
val doubleLength = str?.length?.toDouble()이렇게 chaining하여 사용할 수도 있습니다.
또, Kotlin은 Scope function 이라는 걸 제공합니다.
https://kotlinlang.org/docs/scope-functions.html
private fun doSomething(str: String?) {
str?.let { notNullStr ->
println(notNullStr)
}
}
let 을 사용해서 null 안정성을 확보해도 됩니다.
질문:
str?.length의 타입은 무엇일까요?
?: Elvis operator
세워서 보면 엘비스 프레슬리를 닮아서 이름 지어진 연산자입니다.
?: 널이라면? 으로 해석하시면 편할 거 같습니다.
private fun doSomething(str: String?) {
val str2 = str ?: "Hello"
println(length)
}
str 이 널이라면 str2 에 "Hello" 를 할당한다. 라는 뜻입니다.
자바로 보면 대충 이렇겠죠.
String str2 = "";
if (str == null) {
str2 = "Hello";
} else {
str2 = str;
}
물론 safe call과 함께 이용할 수도 있습니다.val doubleLength = str?.length?.toDouble() ?: 0.0
값 대신 함수를 종료하거나 예외를 던질 수도 있습니다.val str2 = str ?: returnval str2 = str ?: throw Exception()
!! Not-null assertion operator
얘는 절대 null 아님으로 해석하시면 될 거 같습니다.
private fun doSomething(str: String?) {
println(str!!.length)
}
이렇게 하면 컴파일 오류 없이 length 를 호출할 수 있습니다.
하지만 런타임에서 NPE가 터질 확률이 있죠. 그래서 웬만하면 사용하지 말라는 연산자이고,
공식문서에도 for NPE-lovers라는 표현이 나옵니다.
여러분은 어떻게 생각하시나요?
!!은 무조건 쓰면 안되는 걸까요?
'Programming > 언어' 카테고리의 다른 글
| [C++] 선언과 정의는 다르다. (0) | 2024.07.06 |
|---|---|
| [Kotlin] Coroutine 기본 개념, 동작 원리 (0) | 2023.03.19 |
| [Kotlin] Delegation (0) | 2023.03.01 |
| [Kotlin] object, companion object (0) | 2023.02.26 |
| [Kotlin] Generics 공변성, 반공변성(out, in) (0) | 2023.02.26 |