Introduction
Nothing type
問題
常に例外をスローする関数には、戻り値の型として Nothing を使用することができます。
そのような関数を呼び出すと、コンパイラは関数の実行がそれ以降続かないことを前提に処理します。
failWithWrongAge 関数には Nothing 型を戻り値として指定してください。
Nothing 型を指定しない場合、checkAge 関数はコンパイルされません。
これは、コンパイラが年齢が null である可能性を前提としているためです。
解答
import java.lang.IllegalArgumentException
fun failWithWrongAge(age: Int?): Nothing {
throw IllegalArgumentException("Wrong age: $age")
}
fun checkAge(age: Int?) {
if (age == null || age !in 0..150) failWithWrongAge(age)
println("Congrats! Next year you'll be ${age + 1}.")
}
fun main() {
checkAge(10)
}
解説
Kotlin 公式ドキュメント > The Nothing type
The Nothing type
Kotlin では、すべての式に型があります。たとえば、throw IllegalArgumentException()
という式の型は Nothing
です。Nothing
は Kotlin に組み込まれた型で、すべての型のサブタイプ、いわゆる「ボトム型」と呼ばれるものです。これにより、Nothing
は他のどの型が期待される場合でも、戻り値の型やジェネリック型として使用しても型エラーを引き起こすことはありません。
Nothing
は Kotlin で特別な型として扱われ、例外を常にスローしたり、無限ループなどで正常に終了しない関数や式を表現するために使用されます。この型は、まだ実装されていない関数や、常に例外をスローするように設計された関数に使用することで、コンパイラやコードを読む人に意図を明確に伝えることができます。関数のシグネチャでコンパイラが Nothing
型を推論すると警告が表示されますが、戻り値の型として明示的に Nothing
を定義することで、この警告を回避できます。
以下の Kotlin コードは、Nothing
型の使用例を示しています。このコードでは、関数呼び出しの後のコードが到達不可能としてコンパイラに認識される様子が確認できます。
class Person(val name: String?)
fun fail(message: String): Nothing {
throw IllegalArgumentException(message)
// この関数は正常に戻り値を返すことはありません
// 常に例外をスローします
}
fun main() {
// 'name' が null の Person インスタンスを作成します
val person = Person(name = null)
val s: String = person.name ?: fail("Name required")
// 's' はこの時点で確実に初期化されています
println(s)
}
/*
Exception in thread "main" java.lang.IllegalArgumentException: Name required
at FileKt.fail (File.kt:4)
at FileKt.main (File.kt:13)
at FileKt.main (File.kt:-1)
*/
Kotlin の TODO()
関数も Nothing
型を使用しており、今後の実装が必要な箇所を明確にするためのプレースホルダーとして役立ちます。
fun notImplementedFunction(): Int {
TODO("This function is not yet implemented")
}
fun main() {
val result = notImplementedFunction()
// これは NotImplementedError をスローします
println(result)
}
/*
Exception in thread "main" kotlin.NotImplementedError: An operation is not implemented: This function is not yet implemented
at FileKt.notImplementedFunction (File.kt:2)
at FileKt.main (File.kt:6)
at FileKt.main (File.kt:-1)
*/
ご覧のとおり、TODO()
関数は常に NotImplementedError
例外をスローします。
おわりに
Nothing
型って知らなかったな〜。
まだ実装してないよ〜って時に TODO()
を使うのね。
TODO("This function is not yet implemented")
これは関数未実装のときのテンプレとして使いたい!空で書けたらかっこいいんだけどな〜。あ、でも IntelliJ が気を利かせて入れてくれてたような気がしなくもない・・・