KotlinのバイブルでUnitとNothingの違いを読んでいて、
あれ? Javaの仕様ってそうなんだっけ?となったのでメモとして残す。
(忘れてたのはVoidのreturnぐらいだが、せっかくなので関連箇所まとめて整理)
尚、Javaの仕様がきっちり頭に入ってればバイブル本の記載だけで十分である。
Unit
UnitはJavaのvoidとVoidを良いとこ取りしたような型である。
Javaのメソッドで返却値がない場合、voidを指定する。
void print() {
System.out.println("msg");
return;
}
メソッド末尾のreturnは省略可能なので、多くの場合は記載しないが、
処理の途中で返却する場合は明示的にreturnを記載する。
また、returnの代わりにthrowを使うこともある。
void fail() throws Exception {
throw new Exception("hoge");
}
kotlinで同様の内容を書く場合、以下のように記載できる。
fun fail() : Unit {
throw Exception("hoge");
}
ちなみにUnitの指定は省略することができる。
>>> fun hoge() : Unit {}
>>> fun hoge() {}
と、ここまでの用途であればKotlinのUnitはvoidと変わらない。
違いが出てくるのはVoidである。
JavaのGenericsで返却値のないメソッドを扱う場合、以下の2つの方法がある。
- 返却値の有無でクラスやメソッドを使い分ける(例えばCallableやRunnableなど)。
- Voidを使用する。
クラスやメソッドが返却値の有無に関わらず共通の場合はVoidを使用する。
VoidはObjectの子クラスで、インスタンスを生成できない。
また、voidでもないため、返却値をreturnする必要があり、
インスタンスを生成できないVoidは必然的にnullを返すことになる。
Void fail(int i) {
return; // NG. return nullと書くかthrowでないとビルドエラー
}
Void fail(int i) {
return null; // OK
}
KotlinのUnitは上記のようなコードをvoidのように記載することができる。
interface Hoge<T> {
fun run() : T
}
class Fuga : Hoge<Unit> {
override fun run() {
println("hoge");
}
}
Nothing
KotlinではUnitとは別に返却値を返さない場合に使える便利な型がある。
それがNothingである。
Javaでは以下のようなコードを書くことができない。
boolean is1(int i) { return i == 1; }
void fail() throws Exception {
throw new Exception("hoge");
}
void exec() throws Exception {
String s = is1(1) ? "hoge" : fail(); // fail()がStringを返さないのでNG
}
このような処理を書く場合、Javaでは以下のように代入とfail()の呼び出しを分ける必要がある。
boolean is1(int i) { return i == 1; }
void fail() throws Exception {
throw new Exception("hoge");
}
void exec() throws Exception {
String s;
if (is1(1)) {
s = "hoge";
} else {
fail(); // fail()がStringを返さないのでNG
}
}
しかしKotlinのNothingを使用すると以下のように記載することができる。
fun Int.is1() : Boolean = this == 1
fun fail() : Nothing = throw Exception("hoge")
var s:String = if (1.is1()) "hoge" else fail()
Unit と Nothing の違い
UnitとNothingはJavaのvoid/Voidを使用していた用途で使用するが
使用方法に違いがある。
例えばUnitは前述したNothingのように以下のような記載はできない。
(以下はkotlinc-jvmの実行)
>>> fun Int.is1() : Boolean = this == 1
>>> fun fail1() { throw Exception("hoge") }
>>> fun fail2() : Nothing = throw Exception("hoge")
>>>
>>> var s:String = if (1.is1()) "hoge" else fail1()
error: type mismatch: inferred type is Unit but String was expected
var s:String = if (1.is1()) "hoge" else fail1()
^
>>>
>>> var s:String = if (1.is1()) "hoge" else fail2()
>>> var s:String = if (2.is1()) "hoge" else fail2()
java.lang.Exception: hoge
at Line_14.fail2(Line_14.kts:1)
>>>
逆にNothingはUnitのようにthrow以外で終わるコードを記載できない。
>>> fun fail1() {println("hoge")}
>>>
>>> fun fail2() : Nothing {println("hoge")}
error: a 'return' expression required in a function with a block body ('{...}')
fun fail2() : Nothing {println("hoge")}
>>>
また、定義方法としてもそれぞれ以下のような違いがある。
>>> fun fail1() : Unit { throw Exception("hoge") } // OK
>>> fun fail1() { throw Exception("hoge") } // OK
>>> fun fail1() = throw Exception("hoge") // NG
>>> fun fail2() : Nothing = throw Exception("hoge") // OK
>>> fun fail2() : Nothing { throw Exception("hoge") } // OK