Si j'ai une fonction Kotlin
fun f(cb: (Int) -> Unit)
et je veux appeler f
depuis Java, je dois le faire comme ceci:
f(i -> {
dosomething();
return Unit.INSTANCE;
});
qui a l'air très moche. Pourquoi ne puis-je pas simplement l'écrire comme f(i -> dosomething());
, puisque Unit
en Kotlin est équivalent à void
en Java?
Unit
en Kotlin est généralement équivalent à void
en Java, mais uniquement lorsque les règles de la JVM le permettent.
Les types fonctionnels dans Kotlin sont représentés par des interfaces telles que:
public interface Function1<in P1, out R> : Function<R> {
/** Invokes the function with the specified argument. */
public operator fun invoke(p1: P1): R
}
Lorsque vous déclarez (Int) -> Unit
, du point de vue de Java, cela équivaut à Function<Integer, Unit>
. C'est pourquoi vous devez renvoyer une valeur. Pour contourner ce problème, Java comporte deux interfaces distinctes, Consumer<T>
et Function<T, R>
, lorsque vous n’avez pas/n’ont pas de valeur de retour.
Les concepteurs de Kotlin ont décidé d’abandonner la duplication d’interfaces fonctionnelles au profit du compilateur "magique". Si vous déclarez un lambda dans Kotlin, vous n'avez pas à renvoyer de valeur car le compilateur en insère une pour vous.
Pour vous simplifier un peu la vie, vous pouvez écrire une méthode d'assistance qui enveloppe un Consumer<T>
dans un Function1<T, Unit>
:
public class FunctionalUtils {
public static <T> Function1<T, Unit> fromConsumer(Consumer<T> callable) {
return t -> {
callable.accept(t);
return Unit.INSTANCE;
};
}
}
Usage:
f(fromConsumer(integer -> doSomething()));
Anecdote: le traitement spécial de Unit
par le compilateur Kotlin est la raison pour laquelle vous pouvez écrire du code tel que:
fun foo() {
return Unit
}
ou
fun bar() = println("Hello World")
Les deux méthodes ont le type de retour void
dans le bytecode généré, mais le compilateur est suffisamment intelligent pour comprendre cela et vous permettre d'utiliser les instructions/expressions de retour de toute façon.
J'utilise cette approche pour Kotlin et Java. Les méthodes de MyKotlinClass que vous verrez en Java, tandis que dans Kotlin, vous verrez les deux méthodes (méthode de classe + fonction d'extension) .
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) })
}