관리 메뉴

Bbaktaeho

코틀린(Kotlin) 13. NULL 처리, NULL 연산자 본문

프로그래밍 (Programming)/코틀린 (Kotlin)

코틀린(Kotlin) 13. NULL 처리, NULL 연산자

Bbaktaeho 2019. 2. 2. 17:14
반응형

자바에서 런타임 시 발생할 수 있는 NullPointerExeption을 코틀린에서 런타임이 아닌 컴파일 시점에서 미리 방지할 수 있게 했다.

여기서 NullPoiinterExeption은 객체를 참조하는 변숫값이 null이라서 해당 객체의 메서드를 호출하거나 데이터를 참조할 수 없을 때 발생하는 예외이다. 쉽게 말해 빈 저금통에 동전을 찾는 느낌..?

null 타입)

널이 가능한 타입이다. 모든 타입 이름 끝에 ? 을 붙이면 된다.

예를 들어 val a : Int? = null 코드를 보면 변수 a가 Int 타입으로 보이지만 ? 때문에 null이 가능한 Int 타입이라고 할 수 있다.

다른 코드를 보자.

1
2
3
4
5
6
7
8
9
fun main() {
    var a : Int = 10
    a = null //에러
    
    var b : Int= 10
    b = null
    
    b = a
}
cs

위 코드에서 에러가 나는 곳은 한 곳이다.

a = null 에서 에러가 나는데 이유는 null 값을 허용하지 않는 타입에 null을 지정했기 때문이다.

그럼 이제 저금통에서 동전을 빼보자.

1
2
3
4
5
6
fun main() {
    var a : String= "저금통"
    print(a.length) //에러
    
    if (a != null) print(a.length) //정상 출력
}
cs

변수 a는 null이 가능한 String 타입이다. 그러면 변수 a가 null이 될 수도 있다는 말이 된다. 그러므로 a.length 를 호출할 때 a가 null 일수도 있어서 안전하지 않다. 따라서 null이 아닐 때만 호출할 수 있게 a != null 일 때만 사용할 수 있게 하는 것이다.

null 처리 연산자 "?." )

"?." 연산자는 안전 호출 연산자라고도 한다. 객체의 속성이나 함수를 안전하게 호출할 수 있게 해 준다. 즉, 객체를 참조하는 변수의 값이 null이 아닐 때만 호출하게 해주는 것이다.

위의 코드를 수정하면 더 간결해진다.

1
2
3
4
5
fun main() {
    var a : String= "저금통"
    print(a?.length)
}
cs

변수 a가 "저금통" 으로 초기화돼있는데 만약 null 이라면 print(a.?length) 에서 null 값을 반환 할 것이다. 즉, a의 값이 null이 아닐 경우 정상적으로 a.length가 실행되고 null일 경우 null을 반환한다.

null 처리 연산자 "?:" )

"?:" 연산자는 엘비스 연산자라고 한다. 이유는 엘비스 프레슬리의 이모티콘과 비슷해서 지어진 이름이다. 중요한 건 아니다.

이 연산자는 왼쪽 피연산자 값이 null이 아니면 그 피연산자의 결괏값을 반환하고 null이면 오른쪽 피연산자의 결괏값을 반환한다.

val a = 왼쪽 ?: 오른쪽

예시를 보자.

1
2
3
4
5
6
fun main() {
    var a : String= "저금통"
    val b = a?.length ?: 0
    //val b = if (a!=null) a.length else 0
}
cs

주석으로 처리된 if 문과 같은 의미이다. 여기서 피연산자들은 변수, 표현식, 함수 호출 등 모두 가능하다.

null 처리 연산자 "!!" )

null이 될 수 없다는 것을 단언하는 연산자이다. 왼쪽 피연산자 값이 null이 아니면 정상적으로 수행하고 null이면 NullPointerExeption 예외를 발생시킨다. 이 연산자는 컴파일 시점에서 미리 방지해 주지 못한다. 그러면 이 연산자는 왜 필요할까?

예를 들어 null 가능 타입의 변숫값이 null이 아닌지 한 함수에서 확인한 후 다른 여러 함수에서 그 값을 받아 사용한다면 null 확인을 다시 할 필요가 없다. 이럴 때 "!!"연산자를 사용하면 편리하다. null이 아니라는 걸 단언하는 것이다.

또 런타임 시에 시스템 라이브러리의 변수를 참조하거나 함수들을 호출할 때 언제 발생할지 모르는 NullPointerException을 우리가 명시적으로 파악하고자 할 때 필요하다.

코드를 보자.

1
2
3
4
5
6
7
fun main() {
    var a : String= "저금통"
    var b : String= null
    println(a!!.length)
    println(b!!.length) //런타임 에러
}
cs

위에 코드는 컴파일 시에 에러가 나지 않고 런타임 시에 에러가 발생한다.

as, as? 연산자)

타입 변환 연산자이다. 기본 타입 간에 변환은 변환 함수를 사용하고 as 연산자는 사용할 수 없다. toInt(), toDouble() 등등 함수를 쓰면 된다.

as를 사용한 코드를 보자.

1
val a : String = b as String
cs

코드를 해석하면 변수(객체) b를 String 타입으로 변환 후 변수 a에 대입한다. 여기서 b의 값이 String 타입에 적합하다면 성공적인데 만약 부적합하거나 null이라면 ClassCastException 예외가 발생한다.

그러나 as? 연산자를 사용하여 안전하게 타입 변환을 할 수 있다.

1
val a : String = b asString
cs

b가 String 타입에 적합하지 않거나 null 일 경우 변수 a에 null 이 대입된다.


반응형