코틀린에서 가장 중요한 부분인 타입 시스템(type system)을 살펴보도록 하겠습니다.
자바와 비교했을 때 코틀린의 타입 시스템은 코드의 가독성을 향상시키는 데 도움이 되는 몇 가지 특성이 있습니다.
그런 특성으로, null이 될 수 있는 타입(nullable type) 과 읽기 전용 컬렉션이 있습니다.
null 가능성
null 가능성은 NullPointerException(NPE) 을 피할 수 있는 코틀린 타입 시스템의 특성입니다.
NullPointerException 은 앱을 중지 시킬 정도로 굉장히 크리티컬 해서 사용자와 개발자 모두를 당황시키는 존재죠...😨😱
코틀린을 비롯한 최신 언어에서는 이런 null에 대한 이슈를 해결하기 위해, null이 될 수 있는 여부를 타입 시스템에 추가함으로써 런타임 단계가 아닌 컴파일 단계에서 오류를 미리 감지해서 예외를 줄일 수 있도록 하였습니다.
이러한 코틀린의 null이 될 수 있는 타입에 대해 알아보고, 이 값을 어떻게 표기하며 코틀린에서는 어떻게 처리하는지에 대해 살펴보도록 하겠습니다.
null이 될 수 있는 타입
코틀린과 자바의 가장 중요한 차이는 코틀린 타입 시스템이 null이 될 수 있는 타입을 명시적으로 지원한다는 점입니다.
이 말은 즉, null이 될 수 있는 타입은 프로퍼티나 변수가 null을 허용하게 만드는 방법입니다.
만약 자바의 경우 어떤 변수가 null이 될 수 있다면 NullPointerException이 발생할 수 있으므로 안전하지 않은 호출이라고 할 수 있습니다.
코틀린은 그런 호출을 금지함으로써 많은 오류를 방지하게끔 해줍니다! 👍
먼저 자바와 비교하기 위해 null이 될 수 있는 자바 코드를 살펴보겠습니다.
/* 자바 null이 될 수 있는 경우 */
int strLen(String s) {
return s.length();
}
문제점이 한눈에 바로 보이실 겁니다.
네 맞습니다~ 이 함수에 파라미터로 null이 들어오게 되면 NullPointerException이 발생하게 됩니다.
이 함수를 코틀린으로 다시 작성해보겠습니다.
이런 함수를 작성할 때 가장 먼저 생각해야 하는 고민은 "이 함수가 null을 파라마티로 받을 수 있는가?" 입니다.
null이 들어올 수 없다면 다음과 같이 정의할 수 있습니다.
/* 코틀린 null을 허용하지 않는 파라미터 */
fun strLen(s: String) = s.length
위의 코틀린 strLen 함수에 null이거나 null이 될 수 있는 파라미터를 넘기는 것은 제한되며, 만약 그런 값을 넘기면 컴파일 시 오류가 발생합니다.
>>> strLen(null)
ERROR: Null can not be a value of a non-null type String
이 함수가 null값을 허용할 수 있게 하려면 타입 이름 뒤에 물음표(?)를 명시해주면 됩니다.
/* 코틀린 null을 허용하는 파라미터 */
fun strLenSafe(s: String?) = ...
String?, Int?, User? 등 어떠 타입이든 타입 이름 뒤에 물음표(?)를 붙이면 그 타입의 변수나 프로퍼티에 null을 허용할 수 있다는 뜻이 됩니다.
Type? = Type 또는 null 허용
반대로 물음표가 없는 타입은 그 변수가 null 참조를 할 수 없다는 뜻입니다.
따라서 모든 타입은 기본적으로 null이 될 수 없는 타입이고, 뒤에 ? 가 붙어야만 null이 될 수 있습니다.
null이 될 수 있는 타입의 변수는 수행할 수 있는 연산이 제한되는데요, 예를 들어 null이 될 수 있는 변수에 대해 변수.메소드() 처럼 메소드를 직접 호출할 수는 없습니다.
/* null이 될 수 있는 변수에 대해 변수.메소드() 호출은 불가능 하다 */
>>> fun strLenSafe(s: String?) = s.length()
ERROR: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type kotlin.String?
null이 될 수 있는 값을 null이 될 수 없는 변수에 대입하는 것도 불가능합니다.
/* null이 될 수 있는 값을 null이 될 수 없는 변수에 대입할 수 없다 */
val x: String? = null
var y: String = x
ERROR: Type mismatch: inferred type is String? but String was expected
안전한 호출(safe call) 연산자 "?."
코틀린이 제공하는 가장 유용한 기능 중 하나가 안전한 호출 연산자인 "?." 입니다.
"?."은 null 체크와 메소드 호출을 한 번의 연산으로 수행합니다.
예를 들어 user?.getId() 는 if (user != null) { user.getId() } else { null } 과 동일합니다.
호출하려는 값이 null이 아니면 그대로 메소드가 호출되고, 호출하려는 값이 null이면 이 호출은 무시되면서 null값을 반환하게 됩니다.
그렇기 때문에 null 체크를 연달아해야 하는 경우도 코틀린에서는 훨씬 간결하게 null 체크를 할 수 있습니다.
/* null 체크가 연달아 필요한 경우 */
val country = user?.address?.country
엘비스 연산자 "?:"
코틀린은 null 대신 사용할 디폴트 값을 지정할 때 편리하게 사용할 수 있는 연산자를 제공합니다.
그 연산자는 엘비스 연산자 "?:" 입니다.
이 연산자는 좌항의 값이 null이 아니면 좌항 값을 그대로 반환하고, 만약 좌항 값이 null이면 우항 값을 디폴트 값으로 반환합니다.
/* 엘비스 연산자 사용 예제 */
fun getUserType(type: String?) {
val userType: String = type ?: "guest" //type이 null이면 디폴트값은 guest 이다.
}
엘비스 연산자를 안전한 호출 "?." 연산자와 함께 사용해서 객체가 null인 경우에 대비하여 사용하는 경우도 많습니다.
/* 엘비스 연산자를 활용해서 null값 다루기 */
val abTestType = abTestData?.type ?: defaultType
또한, 엘비스 연산자의 우항에는 return, throw 등의 결과도 넣을 수 있어서, 좌항의 값이 null이면 함수 리턴 값을 즉시 반환하거나 예외를 던지는 등의 프로그래밍이 가능합니다.
/* 엘비스 연산자에 return, throw 사용 예제 */
val userId = user?.getId() ?: return null
val userId = user?.getId() ?: throw IllegalArgumentException("No ID")
'코틀린' 카테고리의 다른 글
코틀린(Kotlin) - 타입 파라미터의 null 가능성 (0) | 2021.03.30 |
---|---|
코틀린(Kotlin) - 타입 시스템 as?, !!, let, lateinit (0) | 2021.03.24 |
코틀린(Kotlin) - lambda with receiver(수신 객체 지정 람다) : with, apply (0) | 2021.03.16 |
안드로이드 코루틴 기본 개념과 활용까지의 모든 것! (3) | 2021.03.08 |
코틀린(Kotlin) - 함수형 인터페이스 활용 (0) | 2021.02.24 |