티스토리 단축키 확인 ctrl + '/' 키
코드블록 단축키 alt+ctrl+','
널(Null) 안정성
널(Null)이란? 객체가 선언되었지만 초기화되지 않은 상태
객체는 데이터가 저장된 주소를 참조하므로 흔히 '참조 변수'라고 함. 데이터가 메모리에 저장되면 어디에 저장됐는지 알아야 이용할 수 있는데, 이때 해당 메모리 위치를 식별하는 것이 '주소'.
정리하면, 객체가 메모리에 담긴 데이터를 이용하기 위해, 객체에는 데이터가 직접 저장되는 것이 아닌 데이터가 저장된 메모리의 '주소'가 저장된다. 그 주소로 메모리에 접근해서 데이터를 이용한다. 그런데 널은 객체가 주소를 가지지 못한 상태를 나타낸다.
다음 코드에서는 data1 변수에 "hello"라는 데이터를 저장했다. 하지만 실제로는 "hello"라는 문자열 데이터가 저장된 '주소'가 대입되며, 그 주소로 문자열 데이터를 이용한다.
data2 변수에는 null을 대입했다. 이렇게 하면 주솟값을 가지지 못한다. 즉, 변수가 선언되었지만 이용할 수 없는 상태이다.
// 객체에 널 대입
val data1: String = "hello"
val data2: String? = null // ?는 null도 들어갈 수 있다는 뜻으로 붙임
이처럼 널인 상태의 객체를 이용하면 널 포인트 예외(NullPointException)가 발생한다. 널 포인트 예외는 널인 상태의 객체를 이용할 수 없다는 오류이다.
이때 널 안정성이란 널 포인트 예외가 발생하지 않도록 코드를 작성하는 것을 말한다. 널 포인트 예외는 오류이므로 당연히 발생하지 않게 작성해야 하지만 중요한 건 이런 노력을 누가 하는가이다.
// 널 안정성을 개발자가 고려한 코드 예시
fun main() {
var data: String? = null // ?를 붙여서 변수가 null을 포함할 수 있음
val length = if (data == null) { // if문을 이용해서 데이터가 널이라면
0 //을 반환하고
} else { // 아니라면
data.length // 데이터의 길이를 반환하고 이를 변수에 저장해라(자동 Int타입의 변수가 되는건가?)
}
pritnln("data length : $length")
}
위 소스를 보면 data가 null일 때 data.length 코드를 실행하면 널포인트예외가 발생한다. 그런데 이런 노력을 개발자가 이렇게 일일이 하지 않을 수 있도록 코틀린이 널안정성을 위한 도구를 제공한다.
// 코틀린이 제공하는 널 안정성 연산자를 이용한 코드
fun main() {
var data: String? = null
println("data length : ${data?.length ?: 0}") // null에 .length를 어케쓰지... 그래서 leng?th인건가.
// 물음표를 이중으로 붙여야하면 이건 개발자의 몫인가
// data가 null이면 0을 반환하고, null이 아니면 length를 이용해 문자열의 개수를 얻는다.
}
실행결과:
data length : 0
위의 코드는 앞선 코드와 똑같이 동작하지만 코드에서 null을 점검하지는 않는다. 즉, null점검 코드를 작성하지 않았는데도 널 안정성을 확보했다.
이처럼 프로그래밍 언어가 널 안정성을 지원한다는 것은 객체가 널인 상황에서 널 포인트 예외가 발생하지 않도록 연산자를 비롯해 여러 기법을 제공한다는 의미이다.
널 안정성 연산자
널 허용 - ? 연산자
복습) 코틀린에서는 변수 타입을 널 허용(nullable)과 널 불허용(not null)로 구분한다. 즉, 변수에 null을 대입할 수 있는지를 '선언할 때 타입으로 구분'한다.
// 널 허용과 널 불허
var data1: String = "kkang"
data1 = null // 오류!(널 불허라서)
var data2: String? = "kkang" // ?연산자 활용하여 변수를 널허용으로 선언
data2 = null // 성공!
널 안정성 호출 - ?. 연산자
널 허용이 아닌 널 불허인 변수는 여전히 널 포인트 예외가 발생할 것을 신경써야 한다.
// 널 포인트 예외 오류
var data: String = "kkang"
var length = data.length // 오류!
이처럼 null이 대입될 수 있는 변수를 널 안정성을 고려하지 않고 작성하면 오류가 발생한다.
따라서 널 허용으로 선언한 변수의 멤버에 접근할 때는 반드시 ?.연산자를 사용해야 한다.
?. 연산자는 변수가 null이 아니면 멤버에 접근하지만 null이면 멤버에 접근하지 않고 null을 반환한다.
// 널 안정성 호출 연산자 사용
var data: String? = "kkang"
var length = data?.length
?. 연산자 덕분에 널 허용 변수에 null이 대입되더라도 멤버에 접근하지 않고 null을 반환하므로, 널포인트예외는 발생하지 않습니다.
엘비스 - ?:연산자
엘비스 연산자 ➡️ ?: 기호
이 연산자는 변수가 널이면 널을 반환한다.
그런데 어떤 경우에는 변수가 널일 때, 대입해야 하는 값이나 실행해야 하는 구문이 있을 수 있다. 이때 엘비스 연산자를 사용한다.
// 엘비스 연산자 사용
fun main() {
var data: String? = "kkang"
println("data = $data : ${data?.length ?: -1}")
// ?: 좌측의 변수가 null이면 우측의 값을 반환하라
data = null
println("data = $data : ${data?.length ?: -1}")
}
실행결과:
data = kkang : 5
data = null : -1
예외 발생 - !! 연산자
!! ➡️ 객체가 널일 때 예외(exception)를 일으키는 연산자
객체가 널일 때 ?. 또는 ?: 연산자를 이용해 널 포인트 예외가 발생하지 않게 작성할 수도 있지만, 또 어떤 경우에는 널 포인트 예외를 발생시켜야 할 때도 있다. 이때 !! 연산자를 사용한다.
// 예외 발생 연산자
fun some(data: String?): Int {
return data!!.length
}
fun main() {
println(some("kkang")) // 함수에 문자열 전달하면 오류없이 출력
println(some(null)) // 함수에 null 전달하면 예외메시지 출력함
}
실행결과:
5
Exception in thread "main" java.lang.NullPointerException
'깡샘 코틀린' 카테고리의 다른 글
06-2 뷰 클래스 (0) | 2023.06.15 |
---|---|
06-1 화면을 구성하는 방법 (2) | 2023.06.15 |
05-1 람다 함수와 고차 함수 (0) | 2023.06.15 |
04-3 코틀린의 클래스 종류 (0) | 2023.06.14 |
04-2 클래스를 재사용하는 상속 (0) | 2023.06.14 |