web-dev-qa-db-ja.com

Java Unitを返すKotlin関数を実装するときにUnit.INSTANCEを返す必要があるのはなぜですか?

Kotlin関数がある場合

_fun f(cb: (Int) -> Unit)
_

javaからfを呼び出したいので、次のようにしなければなりません。

_f(i -> {
     dosomething();
     return Unit.INSTANCE;
});
_

これは非常にいです。 KotlinのUnitはJavaのvoidと同等なので、なぜf(i -> dosomething());のように書けないのですか?

34

KotlinのUnitは、Javaのvoidとほぼ同等ですが、JVMのルールで許可されている場合のみです。

Kotlinの機能タイプは、次のようなインターフェースで表されます。

public interface Function1<in P1, out R> : Function<R> {
    /** Invokes the function with the specified argument. */
    public operator fun invoke(p1: P1): R
}

(Int) -> Unitを宣言すると、Javaの観点からすると、これはFunction<Integer, Unit>と同等です。そのため、値を返す必要があります。この問題を回避するために、Javaに戻り値がない場合/戻り値がある場合のための2つの個別のインターフェースConsumer<T>Function<T, R>があります。

Kotlinの設計者は、機能的なインターフェイスの重複を控え、代わりにコンパイラの「魔法」に依存することにしました。 Kotlinでラムダを宣言する場合、コンパイラが値を挿入するため、値を返す必要はありません。

生活を少し楽にするために、Consumer<T>Function1<T, Unit>でラップするヘルパーメソッドを作成できます。

public class FunctionalUtils {
    public static <T> Function1<T, Unit> fromConsumer(Consumer<T> callable) {
        return t -> {
            callable.accept(t);
            return Unit.INSTANCE;
        };
    }
}

使用法:

f(fromConsumer(integer -> doSomething()));

楽しい事実:KotlinコンパイラーによるUnitの特別な処理が、次のようなコードを記述できる理由です。

fun foo() {
    return Unit
}

または

fun bar() = println("Hello World")

どちらのメソッドも、生成されたバイトコードに戻り型voidがありますが、コンパイラーはそれを把握し、とにかくreturnステートメント/式を使用できるようにするのに十分スマートです。

44
Kirill Rakhman

私はこのアプローチをKotlin&Javaに使用します。 Javaで表示されるMyKotlinClassのメソッド、Kotlinで両方のメソッド(クラスメソッド+拡張関数)が表示されます。

MyKotlinClass {

  //Method to use in Java, but not restricted to use in Kotlin.
    fun f(cb: Consumer<Int>) { //Java8 Consumer, or any custom with the same interface
      int i = getYourInt()
      cb.accept(i)
    }
}

//Extension for Kotlin. It will be used in Kotlin.
fun MyKotlinClass.f(cb: (Int) -> Unit) {
    f(Consumer { cb(it) })
}
0
ultraon