-
나만 몰랐었던 NothingKotlin 2025. 4. 30. 16:00
안녕하세요! Mash-Up Android 플랫폼에서 활동 중인 조재훈입니다.
오늘은 Kotlin의 특별한 타입인
Nothing
에 대해 알아보려고 합니다. 본격적으로Nothing
을 살펴보기 전에,Any
와Unit
부터 간단히 짚고 넘어가겠습니다.
코틀린 최상위 타입 : 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
입니다. 그런데name
은String?
타입이고,throw IllegalArgumentException(...)
의 타입은Nothing
이므로 컴파일 에러가 발생할 것 같습니다.하지만,
Nothing
이 모든 타입의 하위 타입이므로 우항의Nothing
이String
으로 자동 추론됩니다.따라서 컴파일러는
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 in Action
- https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-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