-
Unit / Nothing🔖 Kotlin 2024. 11. 25. 12:20
🎯 목표
코틀린에는 return 하지 않아도 되는 반환 유형이 Unit 과 Nothing 두 가지가 존재한다.
이 두 가지의 차이점을 알아보고 언제 어떤 반환 유형을 사용해야 할지 공부해보려고 한다.
1️⃣ Unit
Kotlin 에서 반환 유형을 지정하지 않은 경우 기본적으로 반환 유형이 Unit이 된다.
- Java의 void와 비슷하지만, Kotlin에서는 Unit도 실제로 객체로 존재
특징
- Unit은 타입이면서 싱글톤 인스턴스로 매번 객체를 생성하지 않는다.
- 함수의 반환값이 없더라도 Unit을 명시적으로 반환할 수 있다.
- 기본적으로 반환값이 없는 함수는 Unit을 암시적으로 반환
fun printMessage(message: String): Unit { println(message) } // 아래는 동일 fun printMessage(message: String) { println(message) }
2️⃣ Nothing
Nothing 은 값을 반환하지 않는 경우를 나타내는 타입입니다.
- 함수가 정상적으로 실행을 종료하지 않거나, 절대로 실행 흐름이 도달하지 않는 위치를 표시합니다.
특징
- Nothing 타입을 반환하는 함수는 정상적으로 반환하지 않고, 예외를 던지거나 프로그램이 종료
- "실행 흐름이 끝나지 않는" 코드 위치를 나타내는 데 사용.
- Nothing은 모든 타입의 하위타입으로, 모든 타입과 호환된다.
📖 ”실행 흐름이 끝나지 않는 코드” 위치
- 코드가 값을 반환하지 않거나, 실행이 멈추거나, 예외가 발생하여 정상적으로 종료되지 않는 위치를 의미
- 즉, 이후 코드가 실행되지 않는 구역을 가리키며, 이는 다음과 같은 상황에서 발생할 수 있다.
- 예외를 던지는 코드 (throw)
- 프로그램을 강제로 종료하는 코드 (exitProcess)
- 무한 루프
🔖 정리
특징 Unit Nothing 의미 "아무 값도 반환하지 않는다." "실행 흐름이 이 위치에서 멈춘다." 실행 흐름 함수가 정상적으로 실행을 끝낸 후 흐름을 계속 진행함 실행 흐름이 중단되고 이후 코드에 도달하지 않음 사용 목적 반환할 값이 없을 때 사용 (e.g., println()) 함수가 절대 반환되지 않음을 나타냄 (e.g., throw, exitProcess) 컴파일러 처리 반환 타입으로 Unit을 추론 이후 코드가 도달 불가능함을 컴파일러가 알 수 있음 📌 Nothing의 활용
1️⃣ 예외를 던지는 함수
코틀린에서 throw는 표현식으로, Elvis 표현식의 일부로 사용할 수 있다.
val grade = beomsic.grade ?: throw RuntimeException("")
Nothing은 실행 흐름이 도달할 수 없는 구역을 나타내기 위한 특수 타입으로, throw 표현식은 Nothing 타입을 갖는다.
throw 표현식은 항상 실행 흐름을 중단하기 때문에 반환값이 없습니다.
따라서 throw의 타입은 Nothing
fun throwException(message: String): Nothing { throw IllegalArgumentException(message) }
설명
- 위 함수는 항상 예외를 던지므로 정상적으로 종료되지 않는다.
- 반환 타입을 Nothing으로 선언함으로써 이 함수가 절대 값을 반환하지 않는다는 것을 명시적으로 나타낸다.
2️⃣ 도달할 수 없는 코드
Nothing은 실행 흐름이 도달할 수 없는 구역을 표시하는 데 사용.
예를 들어, when 표현식에서 모든 경우를 처리했지만, 여전히 기본값으로 예외를 처리하고 싶을 때 활용
fun getColorCode(color: String): String { return when (color) { "red" -> "#FF0000" "green" -> "#00FF00" "blue" -> "#0000FF" else -> throw IllegalArgumentException("Unknown color: $color") // Nothing 타입 } }
설명
- else 분기는 항상 예외를 던지므로 반환 타입이 Nothing.
- 이는 Kotlin 컴파일러가 "모든 경우가 처리되었다"고 판단하게 한다.
- 컴파일러는 else에서 예외를 던지면, Nothing 타입 때문에 더 이상 실행이 진행되지 않음을 알 수 있다.
3️⃣ 실행을 멈추는 역할
Nothing은 특정 조건에서 실행을 멈추는 역할도 합니다.
예를 들어, 함수에서 프로그램 실행을 종료해야 할 때 사용
fun terminateProgram(): Nothing { println("Program is terminating...") exitProcess(0) // 종료, 아무 값도 반환하지 않음 }
설명
- 이 함수는 종료 후 반환값이 없으므로 Nothing 타입을 반환
4️⃣ 타입 호환성
Nothing은 Any 타입의 하위 타입이므로 어떤 타입에도 대입 가능
예를 들어, throw 표현식이 들어가는 경우 Nothing은 자동으로 다른 타입과 호환된다.
val number: Int = throw IllegalArgumentException("This is an error")
설명
- throw 표현식의 타입은 Nothing이지만, 이는 Int 타입과 호환됩니다.
- 컴파일러는 실행 흐름이 여기서 끝난다는 것을 알기 때문에 오류 없이 코드를 처리
5️⃣ 타입 추론
Kotlin에서 Nothing? 는 null 값만 가질 수 있는 특별한 nullable 타입이다.
컴파일러는 타입을 추론할 정보가 부족하고, 초기화 값이 null인 경우에 그 변수의 타입을 Nothing? 으로 추론한다.
val test = null // Nothing?로 추론 test?.length // 컴파일 오류: Nothing 타입에는 length 속성이 없음
Nothing? 변수에 다른 타입의 값을 할당
val test1 = null // Nothing? 타입 val test2: String? = test1 // Nothing?은 String?에 할당 가능
- Nothing?은 모든 nullable 타입의 하위타입이므로, String? 같은 넓은 범위의 nullable 타입으로 할당할 수 있다.
명시적으로 타입을 지정한 경우
val test: String? = null // test는 명시적으로 String? 타입
- 이 경우, 컴파일러는 타입 정보를 제공받았으므로 Nothing?로 추론하지 않고 String?로 선언
👍 Nothing 을 사용하는 이유와 이점
- 실행 흐름 중단을 명시
- 코드가 정상적으로 끝나지 않음을 나타낸다.
- 예외 처리, 프로그램 종료, 도달할 수 없는 코드의 존재를 명확히 보여준다.
- 모든 타입과 호환
- 어떤 타입과도 호환되므로, 조건문이나 함수 반환값의 일부로 활용
- 코드 가독성 향상
- 코드에서 예외를 던지거나 실행 흐름이 중단되는 위치를 직관적으로 이해 가능
📖 Reference
'🔖 Kotlin' 카테고리의 다른 글
😎 Infix Function (0) 2024.12.30 널 안정성 (0) 2024.12.29 🦥 lateinit 과 lazy (0) 2024.12.29 Kotlin Coroutines (0) 2024.12.28 Gradle Kotlin DSL (3) 2024.11.21