# Table Of Contents

# NotNull 타입

Kotlin은 변수를 초기화하지 않으면 컴파일 자체가 되지 않는다.

var name: String
// 컴파일 자체가 되지 않고 다음과 같은 에러가 발생합
// Property must be initialized or be abstract

클래스의 멤버변수 역시 초기화하지 않으면 컴파일 자체가 되지 않는다.

class Person {
    private var name: String    // Property must be initialized or be abstract
    private var nation: String  // Property must be initialized or be abstract
}

NotNull 타입의 변수는 null로 초기화할 수도 없다.

var name: String = null	
// 컴파일 자체가 되지 않고 다음과 같은 에러가 발생
// Null can not be a value of a non-null type String
class Person {
    private var name: String = null     // Null can not be a value of a non-null type String
    private var nation: String = null   // Null can not be a value of a non-null type String
}

# Nullable 타입

Kotlin에서 null을 할당하기 위해서는 변수를 Nullable타입으로 선언해야 한다. 타입 뒤에 물음표(?)를 붙이면 된다.

var name: String? = null

class Person {
    private var name: String? = null
    private var nation: String? = null
}

물론 Nullable타입이라도 초기화하지 않으면 컴파일 자체가 되지 않는다.

var name: String?   // 다음과 같은 에러가 발생
// Property must be initialized or be abstract
class Person {
    private var name: String?       // Property must be initialized or be abstract
    private var nation: String?     // Property must be initialized or be abstract
}

# Null 체크

Nullable타입의 변수에 접근하려면 값이 null인지 체크해야한다. 그렇지 않으면 컴파일 자체가 되지 않는다.

class Person constructor(val name: String) {
    fun printName() {
        println("name: ${name}")
    }
}

var person: Person? = null

person.printName()  // null 체크를 하지 않았으므로 다음과 같은 에러가 발생한다.
// Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Person?

변수가 null이 아니라고 단언하고 변수에 접근할 수 있다. 이때는 느낌표 두 개(!!)를 사용한다.

var person: Person? = Person()
person!!.printName()    // 성공

다만 값이 null일 때 변수에 접근하면 런타임 에러가 발생한다.

var person: Person? = null
person!!.printName()    // 런타임 에러

따라서 런타임 에러를 방지하기 위해 if구문으로 null체크를 한다.

var person: Person? = null

if (person != null) {
    person!!.printName()
}

var name = if (person != null) person!!.name else ""

# Safe Call

매번 if구문으로 null체크를 하는 것은 번거로운 일이다. 이 때 단축구문인 물음표(?)를 사용할 수 있으며, 이를 Safe Call이라고 한다.

var person: Person? = null

person?.printName()
// person이 null이면 아무 일도 일어나지 않는다.
// person이 null이 아니면 printName()을 호출한다.

var name = person?.name

# Chaining

Nullable타입의 변수가 중첩되어 있는 경우, 다음과 같이 연속적으로 null체크를 할 수 있다.

class Person constructor(val phone: Phone?)

class Phone constructor(val manufacturer: String) {
    fun printManufacturer() {
        print(manufacturer)
    }
}

var person: Person? = Person(Phone("Samsung"))

// Chaining
person?.phone?.printManufacturer()
var manufacturer: String? = person?.phone?.manufacturer

# Elvis 연산자

아래 예제를 살펴봅시다.

var name: String? = null

var length = if (name?.length != null) {
    name?.length
} else {
    0
}

위 예제는 엘비스 연산자(?:)를 사용하여 아래와 같이 단축할 수 있다.

var name: String? = null
var length = name?.length ?: 0

값이 null일 경우 Exception을 발생시키도록 구현할 수 있다.

var name: String? = null

// name?.length가 null이면 NullPointException을 발생
var length = name?.length ?: throw IllegalArgumentException("name is null")

# Scope 함수를 통한 Null 체크

Scope 함수를 Null 체크에 활용할 수 있다.

# 값이 null이 아닐 때 실행할 블럭

class Person constructor(var name: String, var nation: String) { 
    // ... 
}

var person: Person? = null

var name = person?.let { 
    // person != null 이면 아래 2줄이 실행된다.
    println("person is not null.")
    it.name
}

# 값이 null일 때 실행할 블럭

var person: Person? = null

person ?: {
    println("person is null")
}()
var person: Person? = null

person ?: {
    println("person is null")
}.invoke()
var person: Person? = null

person ?: run {
    println("person is null")
}

# 값이 null일 때와 아닐 때 실행할 블럭

var person: Person? = null

person?.let{
    println("person is not null")
} ?: {
    println("person is null")
}()
var person: Person? = null

person?.let{
    println("person is not null")
} ?: {
    println("person is null")
}.invoke()
var person: Person? = null

var result = person?.let {
    println("person is not null")
} ?: run {
    println("person is null")
}