Page History

型 -UnitとNothing-

yhornisse edited this page on 26 Dec 2020

Clone this wiki locally

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