null은 항상 이슈의 중심에 있는데 null로 인해 프로그램 전체, 혹은 앱 전체가 멈출 수 있습니다.
프로그램이 멈출 수 있는 상황을 다음과 같이 코드로 만들어 보겠습니다. (1개의 메서드를 갖고 있는 클래스를 만듭니다.)
class One {
fun print() {
Log.d("null_safety", "can you call me?")
}
}
그리고 onCreate() 메서드 안에서 다음과 같이 one 변수 하나를 선언하고 타입으로 내가 만든 클래스를 지정해둡니다.
그리고 특정 조건이 만족할 때만 선언한 변수에 생성자를 호출해서 저장해두는 조건문 if를 만듭니다.
그리고 변수를 통해 해당 클래스의 메서드를 하나 호출합니다.
var one: One
if (1 > 2){
one = One()
}
one.print()
위 코드에서는 1>2가 false이기 때문에 one 변수는 아무것도 없는 null상태가 됩니다.
이 때 print 메서드를 호출하면 null포인터 예외가 발생하면서 프로그램이 다운됩니다.
물론 위 코드는 안드로이드 스튜디오에서 오류를 발생 시켜 컴파일되지 않도록 막아줍니다.
하지만 코드의 양이 많아지면 이런 상황이 언제든지 발생할 수 있으며 kotlin은 이런 상황을 방지하기 위해서 다양한 안전장치를 만들어 두었습니다.
그 결과물이 Null Safety입니다.
kotlin에서 지정하는 기본 변수는 모두 null이 입력되지 않습니다.
null 값을 입력하기 위해서는 변수를 선언할 때 타입 뒤에 ?(Nullable, 물음표)를 입력합니다.
변수에 null 허용 설정하기
변수의 타입 뒤에 물음표를 붙이지 않으면 null 값을 입력할 수 없습니다.
null 예외를 발생시키고 싶지 않다면 기본형으로 선언합니다.
var nullable: string? // 타입 다음에 물음표를 붙여서 null 값을 입력할 수 있습니다.
nullable = null
var notNullable: String
notNullable = null // 일반 변수에는 null을 입력할 수 없습니다.
함수 파라미터에 null 허용 설정하기
안드로이드의 onCreate() 메서드의 Bundle 파라미터처럼 함수의 파라미터에도 null 허용 여부를 설정할 수 있습니다.
함수의 파라미터가 null을 허용하려면 해당 파라미터에 대해서 null 체크를 먼저 해야만 사용할 수 있습니다.
fun nullParameter(str: String?) { // 파라미터 str에 null이 허용됨
if (str != null) { // null 체크를 해야 str 사용가능
var length2 = str.length
}
}
위 코드 처럼 str 파라미터를 조건문 if에서 null인지 아닌지 체크해야지만 사용할 수 있습니다.
함수의 리턴 타입에- null 허용 설정하기
함수의 리턴 타입에도 물음표를 붙여서 null 허용 여부를 설정할 수 있습니다.
fun nullReturn(): String? {
return null
}
함수의 리턴 타입에 Nullable이 저정되어 있지 않으면 null값을 리턴할 수 없습니다.
안전한 호출:?.
변수를 Nullable로 만들기 위해서 물음표를 사용했습니다.
이제는 ?.(Safe Call, 물음표와 온점)을 사용해서 null 체크를 좀 더 간결하게 하겠습니다.
Nullable인 변수 다음에 ?.을 사용하면 해당 변수가 null일 경우 ?. 다음의 메서드나 프로퍼티를 호출하지 않습니다.
다음 코드에서처럼 문자열의 길이를 반환하는 length 프로퍼티를 호출했는데 str 변수 자체가 null일 경우 length 프로퍼티를 호출하지 않고 바로 null을 반환합니다.
fun testSafeCall(str: String?): Int? {
// str이 null이면 length를 체크하지 않고 null을 반환합니다.
var resultNull: Int? = str.length
return resultNull
}
만약 Safe Call을 사용하지 않았는데 str 변수가 null이라면 프로그램은 다운됩니다.
Null 값 대체하기: ?:
위에서 안전한 호출을 위해서 ?/(safe call)을 사용했습니다.
이제는 ?:(Elvis Operator, 물음표와 콜론)을 사용해서 원본변수가 null일 때 넘겨줄 기본값을 설정해보겠습니다.
다음 코드에서 Safe Call 다음에 호출되는 프로퍼티 뒤에 다시ㅏ ?:을 붙였습니다.
그리고 0이라는 값을 표시했습니다.
이렇게 호출하면 str 변수가 null일 경우 가장 뒤에 표시한 0을 반환합니다.
fun testElvis(str: String?): Int {
// length 오른쪽에 ?:을 사용하면 null일 경우 ?: 오른쪽의 값이 반환 됩니다.
var resultNonNull: Int = str?.length ?: 0
return resultNonNull
}
Nullable(?),Safe Call(?.),Elvis Operator(?:)를 구분하는 법
Nullable
표기법 :선언하는 변수의 타입 다음에 ? 표기
사용 목적: null을 입력받기 위해 사용
사용 예: var nullable: 타입?
safe Call
표기법: 선언된 변수의 이름 다음에 ?. 표기
사용 목적: null일 때?/ 다음에 나오는 속성이나 명령어를 처리하지 않기 위해 사용
사용 예: var result = 변수?. length또는 변수?.프로퍼티?.something
Elvis Operator
표기법: 선언된 변수의 이름 다음에 ?: 표기
사용 목적: null일 때 ?: 다음에 나오는 값을 기본값으로 사용
사용 예: var result = 변수?:0 또는 변수?.프로퍼티?:0
var nullalble: String? = null
var size = nullable.length
Log.d("Nullable", "문자열의 길이 = $size")
컴파일 되지 않습니다. 정상적으로 처리하고 싶다면 두가지 방법이 있습니다. 먼저 size변수를 nullable 변수로 설정하여 Safecall을 사용하거나 Elvis Operrator를 사용하여 해당 null변수의 길이를 구하려고 할 시 특정 값으로 저장되게 합니다.
var nullable: String? = null
var size = nullable?.length
Log.d("Nullable", "문자열의 길이 = $size")
문자열의 길이 = null
var nullalble: String? = null
var size = nullable?.length ?: 33
Log.d("Nullable", "문자열의 길이 = $size")
문자열의 길이 = 33