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 ?: return
val 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 |