Kotlin Unit の特徴をまとめてみた

タピオカ

Unitは、Kotlinで最も頻繁に使用される型の一つでありながら、その存在感は意外と薄いものです。
JavaからKotlinに移行した多くのプログラマーにとって、KotlinがJavaのvoid型ではなく、新たにUnitという概念を導入した理由は疑問に思えるかもしれません。
実際には、Unitを完全に理解していなくても、Kotlinの開発を問題なく進めることは可能です。
しかし、UnitはKotlinの文法設計と密接に関係しており、Kotlin開発において静かに重要な役割を果たしています。

Unit とは

Unit を理解する最も直接的な方法は、ソースコードを読むことです。

package kotlin

/**
 * The type with only one value: the `Unit` object. This type corresponds
 * to the `void` type in Java.
 */
public object Unit {
    override fun toString() = "kotlin.Unit"
}

これはUnitのソースコードです。
ソースコードから分かるように、UnitはKotlinのクラスであり、またはSingleton Objectです。

Unit の用途

Unitは公式に定義された概念であり、何の値も返す必要がない場合の戻り値として使用されて、関数が意味のある値を返さないことを示します。
Javaのvoidに似ています。

Unit と void

Kotlinには本当に値を返さない関数が存在しません
見た目上戻り値なしの関数の戻り値の型、実際にはUnitです。
見た目上戻り値なしように見えるのは、Kotlinが省略された部分を自動的に補完してくれるためです。

例えば、以下の2つのコードは完全に同じであり、戻り値の型としてのUnitと戻り値のUnitオブジェクトは省略できます:

fun main() {
    println("Hello world!")
}

fun main(): Unit {
    println("Hello world!")
    return Unit
}

これがKotlinのUnitとJavaのvoidの本質的な違いです。

なぜこのように設計されているのですか?

このように設計されたメリットは、voidを特別に扱う必要がないことです。

例えば、Javaコードは次のようになります。

abstract class Base<T> {
    public abstract T make();
}

class Impl extends Base<Result> {
    @Override
    public Result make() {
        return new Result();
    }
}

以下のような、Baseを継承したNilクラスの場合、makeメソッドでは「何の値も返す必要がない」ですが、継承とGenericsの制限のために、voidとは異なるVoidを導入する必要があり、Voidクラスとreturn nullを使用して実装されます。

class Nil extends Base<Void> {
    @Override
    public Void make() {
        return null;
    }
}

一方、Kotlinでは、通常の関数と同じ方法で処理され、追加の概念を導入する必要がなくなります。

internal class Nil : Base<Unit>() {
    override fun make() {}
}

同様に、この方法は関数式にも適用できます。

fun run(block: () -> Any) { TODO() }

run { "String" } // () -> String
run { 0 }        // () -> Int
run {}           // () -> Unit

Everything is an Expression

PythonやScalaなどの言語と同じ、Kotlin言語は「Everything is an Expression(一切皆有返回值)」というデザイン理念に基づいています。
これは、Kotlinがvoid型の特殊性を排除する根本的な理由でもあります。

「Everything is an Expression」という設計理念は、void以外にも適用されているところがあります、例えば:

  • 戻り値はあるif-else
fun run(flag: Boolean) {
    val value = if (flag) "Kotlin" else "Java"
    println("Hello $value!")
}

fun run(flag: Int) {
    val value = if (flag == 0) {
        "Kotlin"
    } else if (flag > 0) {
        "Java"
    } else {
        "?"
    }
    println("Hello $value!")
}
  • 戻り値があるwhen expression
fun run(flag: Boolean) {
    val value = when(flag) {
        true -> "Kotlin"
        else -> "Java"
    }
    println("Hello $value!")
}

fun run(flag: Int) {
    val value = when {
        flag == 0 -> "Kotlin"
        flag > 0 -> "Java"
        else -> "?"
    }
    println("Hello $value!")
}

まとめ

Kotlinでは、Unit型が重要な役割を果たしており、関数が意味のある値を返さないことを示しています。
Unit型を一貫して使用することで、Kotlinはvoidの特別性を排除し、関数シグネチャをより統一し、コードをより簡潔に明確にします。

カテゴリー: スマホアプリ初心者

タピオカ