본문 바로가기
Kotlin

코틀린에서 null을 관리하는 방법 [Do it! 코틀린 프로그래밍]

by 서퍼리노 2023. 1. 8.
728x90

코틀린은 변수를 사용할 때 반드시 값이 할당되어야 한다는 원칙이 있습니다.

만약 값이 할당 되지 않은 변수를 사용하면 코틀린에서 오류가 발생합니다.

null이란?

null이란 값이 없는 상태를 의미 합니다.

아직 값이 들어오지 않아 잘 모르는 상태 입니다.

 

코틀린에서 null

코틀린에서는  null 상태인 변수를사용하면 물음표(?) 기호를 사용하여 선업해야 합니다.

물론 해당 변수를 사용할 때에는 null을 검사하고 처리하는 방법까지 고려해야합니다.

 

또한 자료형을 변환시키는 방법도 있습니다.

 

null 사용하기

그렇다면 null을 한 번 사용해보자

fun main() {
	var str1: String = "Hello Kotlin!!"
	str1 = null // 오류 null을 허용하지 않습니다.
	println("str1: $str1")
}

다음과 같이 코드를 작성하게 되면 IntelliJ IDEA에서 빨간 줄로 표시하여 프로그래머에게 알려줍니다.

Error:(5, 12) Kotlin: Null can not be a value of a non-null type String

 

위 코드를 해석해보면

값이 변할 수 있는 var 로 String의 형태로 str1이 선언이 됩니다.

그 이후 str1 에 null을 대입 연산하여 str1에 null값을 바꾸려합니다.

하지만 str1의 형태는 String

즉, null이 허용되지 않는 타입이기에

null을 할당할 수 없어 오류가 발생합니다.

 

그렇다면 null을 처리하려면 어떻게 해야할까요

fun main() {
	var str1: String? = "Hello Kotlin!!"
	str1 = null
	println("str1: $str1")
}

해당 코드에서는 단순히 String 자료형의 뒤에 (?)를 명시하였습니다.

위의 코드처럼 (?)를 명시한 이후에는 아래와 같이 프로그램이 실행됩니다.

 

str1: null

이제 null을 변수에 할당 할 수 있습니다.

위의 사례에서 알 수 있듯이

String과 String?은 null의 허용여부가 다릅니다.

 

이 둘은 서로 다른 자료형이라는 것을 알고 있어야합니다.

 

null 허용한 변수 사용하기

예를 들어 프로그램이 실행되는 중 문자열의 길이를 구할 수 있는 length를

null을 허용한 str1에 사용한다면 어떻게 될까요?

 

str1변수에 문자열이 할당되어있는 상태라면 길이를 구할 수 있겠지만

null이 할당되어 있는 상태라면 NPE가 발생할 것입니다.

앞에서 작성한 코드에서 str1.length를 출력하도록 수정한다면 

fun main() {
	var str1: String? = "Hello Kotlin!!"
	str1 = null
	println("str1 : $str1 str1 length: ${str1.length}") // null을 허용하면 length가 실행될 수 없습니다.
}

코드를 수정하고 나면, 

이런식으로 빨간 줄이 표시가되고,

세이프 콜이나(?.)나 non-null 단정기호(!!.)를 사용해야한다는 팁을 볼 수 있습니다.

str1의 length를 접근하려면 이 2가지 기법 중 하나를 사용해야 합니다.

 

위 기법을 사용해보자

세이프 콜(?.)은 해당 변수의 뒤에 작성하면 됩니다.

같이 세이프 콜(?.)을 사용하여 함수를 실행시키면  프로그램이 잘 실행되는 것을 알 수 있습니다.

 

세이프 콜은

str1을 검사한 다음 null이 아니면 str1의 멤버 변수인 length에 접근하여 값을 읽는 것이고,

만약 아무것도 들어있지 않다면 length에 접근하지 않고, null을 그대로 출력합니다.

 

non-null 단정 기호는

그렇다면 단정기호를 사용하면 어떻게 될까요?

단정기호도 해당 str1변수의 뒤에 작성하면 됩니다.

 

아래와 같이 코드를 작성할 수 있습니다.

 

non-null 단정 기호를 사용하면 컴파일러가 null을 검사하지 않고 그대로 진행합니다. 

따라서 컴파일은 가능할지 몰라도 실행하였을 때  null이라면 NPE를 발생시킵니다.

둘의 차이점은?

둘의 차이점을 정리하자면

세이프 콜은 '널일 수 도 있고, 아닐 수도 있어.'

non-null은 '절대 널이 아님을 내가 보장해.'

라는 식의 느낌입니다. 

 

따라서 non-null 단정 기호를 사용하였을 때 개발자의 실수로 NPE가 발생할 수도 있습니다.

그렇다면 이 null을 사용하려면 어떻게 해야할까??

 

조건문을 활용하여 null을 허용한 변수 검사하기

세이프 콜이나 non-null 단정 기호를 사용하는 방법 대신 조건문으로 null을 허용한 변수를 검사해도됩니다.

 

fun main() {
    var str1: String? = "Hello Kotlin"
    str1 = null
    val len = if(str1 != null) str1.length else -1
    println("str1: $str1 length: ${len}")
}

null인지 아닌지 변수를 확인하여, null이 아닌 경우에만 length를 사용하도록,

null이라면 -1를 출력하는 것입니다.

 

세이프 콜과 엘비스 연산자를 활용해 null을 허용한 변수 사용하기

null을 허용한 변수를 사용하는 다른 방법으로는 세이프 콜과 엘비스(Elvis) 연산자를 함께 사용하면 됩니다.

엘비스 연산자는 변수가 null인지 아닌지 검사하여

null이 아니라면 왼쪽 식을 그대로 실행하고, null이라면 오른쪽 식을 실행합니다.

fun main() {
    var str1: String? = "Hello Kotlin"
    str1 = null
    println("str1: $str1 length: ${str1?.length ?: -1}") // 세이프 콜과 엘비스 연산자 사용
}

위 코드를 실행시키면 

str1: null length: -1

으로 출력됩니다. str1 값을 "Hi!"라는 문자열로 바꾸고 다시 실행시킨다면 

Hi!와 문자열의 길이인 3이 출력됩니다.

 

세이프 콜과 엘비스 연산자를 사용한다면 null인 경우의 반환값을 -1과 같은 특정 값으로 대체할 수 있어

null이 발생하더라도 이를 대비할 수 있어 안전하게 사용가능합니다.

 

또한 코드를 한 줄에 표현할 수 있어 가독성이 좋아집니다.

728x90