1. Nothing 타입
Nothing 타입은 실행 흐름이 도달할 수 없는 구역을 나타내기 위한 특수 타입이다.
fun throwing(): Nothing = throw Exception()
fun main() {
println("start")
// Int 타입의 변수에 Nothing 타입의 표현식을 대입
// Nothing 타입은 어떠한 타입과도 호환되기 때문에 이러한 코드가 가능함
// Int 타입에 Nothing 타입을 대입한다고 해도, throwing 함수가 호출되면 곧바로 예외가 던져져 프로그램이 강제 종료되므로 문제가 되지 않는다.
val i: Int = throwing()
println(i)
validate(-2)
/*
Exception in thread "main" java.lang.Exception: num이 음수입니다.
at Part2_코틀린중급문법살펴보기.ex_nothing_type.MainKt.validate(Main.kt:32)
at Part2_코틀린중급문법살펴보기.ex_nothing_type.MainKt.main(Main.kt:22)
at Part2_코틀린중급문법살펴보기.ex_nothing_type.MainKt.main(Main.kt)
*/
}
throw Exception() 표현식은 Nothing 타입을 갖는다.
코틀린에서는 throw 표현식 문장도 표현식으로 취급한다.
Nothing 타입을 활용하는 상황은 다음과 같다.
fun validate(num: Int) {
val result: Int =
if(num >= 0) num
// Nothing 타입의 표현식이기 때문에 if-else 블록이 Int 타입의 표현식으로 인식됨
// if 블록이 Int, else 블록이 Nothing 타입이면, if-else는 Int 타입을 따라간다.
// 만약 throw Exception 부분이 표현식이 아니라면 Unit 타입이 되어버리므로 서로 호환이 되지않아 사용할 수 없음
else throw Exception("num이 음수입니다.") // 해당 타입은 Nothing 타입
}
2. Nullable 타입과 null
Nullable 이란? null 값을 지정할 수 있는 변수를 뜻한다.
null은 참조 변수가 어떠한 객체도 가리키지 않고 있음을 나타내는 키워드이다.
타입 이름 뒤에 ? 붙이면 변수를 Nullable하게 만들 수 있다.
Byte, Short, Int, Long, Float, Double,Char, Boolean 타입 뒤에 ?를 붙이면
그 변수는 참조 변수가 된다. 즉, 실제 데이터가 스택 영역이 아닌 힙 영역에 생성된다.
fun main() {
var person: Person? = Person("K", 30)
person = null
var num: Int? = null
num = 10
}
if-else 표현식의 한쪽 블록에 null을 지정하면
"Test"는 String, null은 Nothing? 타입이다.
if-else 표현식의 타입은 이 둘이 합쳐진 String?이 된다.
// if-else 표현식의 한쪽 블록에 null을 지정하면 Test는 String, null은 Nothing? 타입이다.
// if-else 표현식의 타입은 이 둘이 합쳐진 String? 이 된다.
val test = if(true) "Test" else null
println(test)
3. 안전한 호출 연산자 - ?.
Nullable한 참조 변수의 프로퍼티와 멤버 함수에 접근하려면 . 대신 ?. 연산자를 반드시 사용해야 한다.
?.은 null 값에 안전한 연산자이다.
아래와 같은 코드가 있을 때, 참조 변수가 null이면 참조 변수?.프로퍼티 표현식이 null 값을 갖게 된다.
참조 변수?.프로퍼티
멤버 함수의 경우, 다음과 같이 참조 변수가 null일 때 멤버 함수를 호출하지 않으며, 참조변수?.멤버함수() 표현식은 null이 된다.
참조 변수?.멤버 함수()
fun main() {
var obj: Building? = null
obj?.print() // obj에 null이 지정되어 있어 멤버 함수 호출 무시됨
obj?.name = "건물" // obj에 null이 지정되어 있어 프로퍼티에 값을 집어넣는 동작 무시(Getter/Setter 함수 호출이기 때문에)
obj = Building()
obj?.name = "서울월드컵경기장"
obj?.date = "2001년 11월 10일"
obj?.area = 21_6712
obj?.print()
}
4. Not-null 단정 연산자 - !!
!! 연산자는 Nullable 타입을 Not-null 타입으로 강제로 캐스팅한다.
fun main() {
var obj: Building? = Building()
obj!!.name = "서울시청" // null이 아니기 때문에 Building 타입으로 캐스팅
println(obj!!.name)
obj = null
//obj!!.print() // obj가 null이기 때문에 KotlinNullPointerException 예외 발생
}
obj 참조 변수가 갓 생성한 Building의 인스턴스를 가리키게 하고 있다.
obj는 Building? 타입, obj!!는 Building 타입이다.
obj가 nuill이 아니기 때문에, obj!!는 무사히 Building 타입으로 캐스팅 된다.
만약 obj가 null이었으면 KotlinNullPointerException 예외가 발생한다.
5. 엘비스 연산자 - ?:
엘비스 연산자는 왼쪽의 피연산자가 null이 아니면 그 값을 그대로 쓰고, null이면 우측의 피연산자로 대체하는 연산자이다. 즉, null이면 우측에 있는 값으로 대체하는 연산자이다.
fun main() {
val number: Int? = null
println(number?:0)
val number2: Int? = 15
println(number2?:0)
val str: String? = null
println(str?:"Hello")
}