본문 바로가기
Kotlin/📕Kotlin 공부

Kotlin 널 안정성(Null Safety)

by yewoneeee 2024. 9. 12.

코틀린 언어의 장점에서 코틀린은 널 안정성을 제공한다고 했는데, 그 이유에 대해서 정리해보려고 한다.

코틀린 공식 문서 참고

 

1. Nullable types, non-nullable types

var a: String = "abc"
var b: String? = "abc"
val al = a.length
val bl = b.length // error -> variable 'b' can be null

코틀린에선 변수를 선언할 때 null을 가질 수 있는 타입과 null을 가질 수 없는 타입을 따로 지정해줄 수 있다.

b와 같이 nullable 타입으로 선언된 경우 에러가 발생한다.

 

b의 메소드나 프로퍼티에 접근하기 위해선 어떻게 해야 할까?

 

2. 조건문에서 null 확인

val b: String? = "Kotlin"
if(b != null && b.length > 0) {
	print("${b.length}")
} else {
	print("Empty string")
}

컴파일러는 조건문에 대한 검사 결과를 추적하고 length 호출을 허용하게 됨

따라서 1번 처럼 null을 확인하지 않고 length에 접근했을 땐 오류가 발생했지만, 조건문으로 null을 확인하게 된다면 length로의 접근이 가능해진다.

 

단, b는 변경 불가능(immutable)할 때만 동작한다. (검증과 사용 사이에 변경 할 수 없는 지역변수, backing field가 있고 오버라이드가 불가능한 val)

왜냐하면 b가 null 이 아님을 검증한 후, null로 변경이 가능하게 된다면 오류가 발생할 수 있기 때문

 

3. safe calls(안전한 호출)

nullable 변수의 프로퍼티에 접근할 때 safe call 연산자 `?.` 를 사용하는 것

val a = "Kotlin"
val b: String? = null
println(b?.length)
println(a?.length) // Unnecessary safe call

b가 null이 아니면 b.length 값을 리턴하고, b가 null이면 null 값을 리턴함

 

safe calls는 chain으로 사용될 때 유용함

bob?.department?.head?.name

만약 이 중 어떤 프로퍼티가 null이면 null을 리턴하게 된다.

 

null이 아닌 값에 대해서만 특정 연산을 수행하려면 안전 호출 연산자인 `let`을 사용

val listWithNulls: List<String?> = listOf("Kotlin", null)
for (item in listWithNulls) {
    item?.let { println(it) } // prints Kotlin and ignores null
}

 

safe calls는 =의 왼쪽에 위치할 수도 있음

// If either `person` or `person.department` is null, the function is not called:
person?.department?.head = managersPool.getManager()

person 또는 department가 중 하나라도 null이라면 오른쪽의 표현식은 스킵됨

 

4. Nullable receiver

확장은 nullable receiver 유형으로 정의할 수 있음

이런 확장은 값이 null이더라도 객체 변수에서 호출할 수 있게 해줌

fun Any?.toString(): String {
    if (this == null) return "null"
    // After the null check, 'this' is autocast to a non-nullable type, so the toString() below
    // resolves to the member function of the Any class
    return toString()
}

 

5. Elvis operator(엘비스 연산자)

val l = b?.length ?: -1
// val l: Int = if (b != null) b.length else -1 와 동일

`?:` - 엘비스 연산자

b.length가 null이 아니면 `?:` 왼쪽의 표현식을 리턴하고, null이면 `?:` 오른쪽의 표현식을 리턴함

fun foo(node: Node): String? {
    val parent = node.getParent() ?: return null
    val name = node.getName() ?: throw IllegalArgumentException("name expected")
    // ...
}

코틀린에선 throw와 return 는 표현식이기 때문에 엘비스 연산자의 오른쪽에 사용할 수 있음

위 코드처럼 null일 때 오류를 throw 할 수 있다

 

6. !! 연산자 (not - null assertion operator)

`!!` 연산자는 모든 값을 null이 불가능한 타입으로 변환하고, 값이 null인 경우 exception(NPE - Null Pointer Exception)을 throw함

val l = b!!.length

 

7. safe casts

일반적인 cast는 객체가 타겟 타입이 아닌 경우에 `ClassCastException`을 발생시키게 됨

safe cast를 사용하면 캐스팅 시도가 성공하지 못했을 때 null을 리턴하게 됨

val aInt: Int? = a as? Int

 

 

8. Collections of a nullable type

nullable 타입의 요소를 저장할 수 있는 컬렉션에서 non-nullable 요소를 필터링 하고 싶을 때 `filterNotNull` 사용

val nullableList: List<Int?> = listOf(1, 2, null, 4)
val intList: List<Int> = nullableList.filterNotNull()

 

 

 

'Kotlin > 📕Kotlin 공부' 카테고리의 다른 글

Kotlin backing field  (0) 2024.09.12
isEmpty, isBlank 차이  (0) 2024.07.27
Kotlin Mutable, Immutable List  (0) 2024.07.26
StringBuilder  (0) 2024.07.20

댓글