ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 나만 몰랐었던 Nothing
    Kotlin 2025. 4. 30. 16:00

     

     

    안녕하세요! Mash-Up Android 플랫폼에서 활동 중인 조재훈입니다.

     

    오늘은 Kotlin의 특별한 타입인 Nothing에 대해 알아보려고 합니다. 본격적으로 Nothing을 살펴보기 전에, AnyUnit부터 간단히 짚고 넘어가겠습니다.

     


    코틀린 최상위 타입 : Any

    Kotlin에서 Any는 Java의 Object에 해당하는 최상위 타입입니다. 하지만 중요한 차이점이 있습니다. Java에서는 int, float 같은 원시 타입(primitive type)은 Object 계층에 포함되지 않지만, Kotlin에서는 Int, Float 같은 원시 타입도 모두 Any를 상위 타입으로 가집니다.

     

    Kotlin의 타입 계층

     

    Kotlin 코드를 바이트코드로 컴파일하면, Any는 Java의 Object와 일치합니다. Java 메서드에서 Object를 인자로 받거나 반환하는 경우, Kotlin에서는 이를 Any로 취급합니다. 다만 Null 여부를 알 수 없기 때문에 정확히는 플랫폼 타입인 Any!로 다룹니다.


    코틀린의 void : Unit

    Unit은 Kotlin에서 반환값이 없는 함수의 반환 타입으로, Java의 void에 해당합니다.

    Unit 타입에 속한 값은 오직 하나, Unit 객체뿐입니다. Unit은 싱글톤 인스턴스이며, 타입이면서 동시에 객체이기도 합니다.

     

    public object Unit {
        override fun toString() = "kotlin.Unit"
    }

     

    Unit은 반환값을 명시적으로 작성하지 않아도 됩니다. Kotlin 컴파일러가 자동으로 Unit을 반환해주기 때문에, 코드가 더욱 간결해집니다.

     

    특히 제네릭 인터페이스를 구현할 때 Unit을 반환 타입으로 사용하는 경우에 유용합니다.

    interface Processor<T> {
        fun process(): T
    }
    
    class NoResultProcessor : Processor<Unit> {
        override fun process() { // Unit을 반환하지만 타입을 지정할 필요 없음
            ...
    	// return을 명시할 필요 없음 (컴파일러가 묵시적으로 Unit을 넣어줌)
        }
    }

    인스턴스가 존재하지 않는 타입 : Nothing

    Nothing

    Nothing has no instances. You can use Nothing to represent "a value that never exists": for example, if a function has the return type of Nothing, it means that it never returns (always throws an exception).

     

    Kotlin 공식 문서에 따르면 Nothing은 인스턴스가 존재하지 않는 타입입니다. 즉, "결코 존재할 수 없는 값"을 나타냅니다.

    어떤 함수의 반환 타입이 Nothing이면, 그 함수는 절대로 정상적으로 반환되지 않습니다. 대신 항상 예외를 던지거나 프로그램을 종료시키는 식으로 동작합니다.

     

    즉, Nothing리턴 자체를 하지 않습니다.

     

    예를 들면,

    fun fail(message: String): Nothing {
        throw IllegalStateException(message)
    }
    

     

    위 함수는 무조건 예외를 던지기 때문에 반환할 수 있는 값이 없습니다. 그래서 반환 타입이 Nothing입니다.

     

    Nothing 타입은 값이 없기 때문에 함수의 반환 타입이나 제네릭의 타입 파라미터로만 주로 사용됩니다. 만약 일반 변수에 Nothing 타입을 선언하더라도 아무 값도 저장할 수 없기 때문에 의미가 없습니다.

     

    val test = throw IllegalStateException()

     

    throw 표현식은 값을 절대 반환하지 않기 때문에 전체 표현식의 타입이 Nothing으로 추론됩니다.

    IDE에서 test에 마우스를 올려보면, 타입이 Nothing으로 표시되는 것을 확인할 수 있습니다.

     

    모든 타입의 하위 타입

    Nothing모든 타입의 하위 타입입니다. Kotlin에서 제공하는 클래스들 외에, 개발자가 만든 클래스들도 Nothing을 하위 타입으로 가집니다. 이 특성 덕분에 타입 추론이나 제네릭 처리에서 매우 유용하게 활용됩니다.

     

    타입 추론

    fun getNameOrThrow(name: String?): String {
        return name ?: throw IllegalArgumentException("이름이 없습니다.")
    }
    

     

    위 함수의 반환 타입은 String입니다. 그런데 nameString?타입이고, throw IllegalArgumentException(...)의 타입은 Nothing이므로 컴파일 에러가 발생할 것 같습니다.

     

    하지만, Nothing 모든 타입의 하위 타입이므로 우항의 NothingString으로 자동 추론됩니다.

     

    따라서 컴파일러는 name ?: throw ... 전체를 String으로 타입 추론할 수 있게 됩니다.

     

    제네릭 처리

    public fun <T> emptyList(): List<T> = EmptyList

     

    리스트를 초기화 할 때 자주 사용하는 emptyList의 반환값은 EmptyList라는 Object이고,

     

    internal object EmptyList : List<Nothing>, Serializable, RandomAccess {
        // ...
    }

     

    EmptyList 내부코드를 보면 List<Nothing> 타입입니다. Nothing은 모든 타입의 하위 타입이므로, 이렇게 어떤 리스트에도 할당할 수 있습니다.

     

    즉, 타입 안전성을 유지하면서도 모든 타입의 빈 리스트 역할을 할 수 있습니다.

     

     

     

    그래서 오늘의 결론 : Any는 모든 타입의 최상위 타입, Nothing은 모든 타입의 최하위 타입


    출처

     

    'Kotlin' 카테고리의 다른 글

    Kotlin Delegation 분석  (0) 2023.03.06
    Kotlinx-Serialization  (0) 2023.03.06
    Scope Function Basic  (0) 2023.03.06
    확장 함수, 람다 함수, 고차 함수의 기초  (0) 2023.03.06
    How do Kotlin coroutines work internally  (0) 2023.03.06
Designed by Tistory.