1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

JavaユーザのためのKotlinのUnitとNothingの違い。Javaのvoidとの違いは?

Last updated at Posted at 2020-12-18

KotlinのバイブルでUnitとNothingの違いを読んでいて、
あれ? Javaの仕様ってそうなんだっけ?となったのでメモとして残す。
(忘れてたのはVoidのreturnぐらいだが、せっかくなので関連箇所まとめて整理)
尚、Javaの仕様がきっちり頭に入ってればバイブル本の記載だけで十分である。

Unit

UnitはJavaのvoidとVoidを良いとこ取りしたような型である。
Javaのメソッドで返却値がない場合、voidを指定する。

Java
void print() {
    System.out.println("msg");
    return;
}

メソッド末尾のreturnは省略可能なので、多くの場合は記載しないが、
処理の途中で返却する場合は明示的にreturnを記載する。
また、returnの代わりにthrowを使うこともある。

Java
void fail() throws Exception {
    throw new Exception("hoge");
}

kotlinで同様の内容を書く場合、以下のように記載できる。

Kotlin
fun fail() : Unit {
    throw Exception("hoge");
}

ちなみにUnitの指定は省略することができる。

kotlinc-jvm
>>> fun hoge() : Unit {}
>>> fun hoge() {}

と、ここまでの用途であればKotlinのUnitはvoidと変わらない。
違いが出てくるのはVoidである。
JavaのGenericsで返却値のないメソッドを扱う場合、以下の2つの方法がある。

  • 返却値の有無でクラスやメソッドを使い分ける(例えばCallableやRunnableなど)。
  • Voidを使用する。

クラスやメソッドが返却値の有無に関わらず共通の場合はVoidを使用する。
VoidはObjectの子クラスで、インスタンスを生成できない。
また、voidでもないため、返却値をreturnする必要があり、
インスタンスを生成できないVoidは必然的にnullを返すことになる。

Java
Void fail(int i) {
    return; // NG. return nullと書くかthrowでないとビルドエラー
}
Java
Void fail(int i) {
    return null; // OK
}

KotlinのUnitは上記のようなコードをvoidのように記載することができる。

Kotlin
interface Hoge<T> {
    fun run() : T
}
class Fuga : Hoge<Unit> {
    override fun run() {
        println("hoge");
    }
}

Nothing

KotlinではUnitとは別に返却値を返さない場合に使える便利な型がある。
それがNothingである。
Javaでは以下のようなコードを書くことができない。

ビルドNG
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()の呼び出しを分ける必要がある。

ビルドOK
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を使用すると以下のように記載することができる。

Kotlin
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の実行)

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以外で終わるコードを記載できない。

kotlinc-jvm
>>> 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")}
>>> 

また、定義方法としてもそれぞれ以下のような違いがある。

kotlinc-jvm
>>> 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
1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?