我有一个完全用Kotlin编写的库,包括其公共API。现在,该库的用户使用Java,这里的问题是返回类型为Unit
的Kotlin Lambdas没有被编译为返回类型void
。结果是Java端必须始终为有效的Unit.INSTANCE
方法返回void
。可以避免这种情况吗?
示例:
Kotlin Lambda
interface Foo{
fun bar(x:(String)->Unit)
}
Java调用
public void call(){
foo.bar(this::processString)
}
//the return type should rather be void instead of Unit
public Unit processString(String s){
return Unit.INSTANCE
// ^^ implementations should not be forced to return anything
}
是否可以以不同的方式声明Kotlin Lambda,以便编译器生成void
返回类型?
另请参阅How to declare a Kotlin function with return type 'void' for a java caller?
答案 0 :(得分:5)
我对此没有真正的答案,但我会分享在需要从Java访问此类Kotlin代码(或我想到的情况)的情况下所做的事情。
基本上,这取决于您真正想要触摸/增强哪一边才能获得所需的内容。
增强Kotlin代码以支持Java等效项:
interface Foo {
fun bar(x : (String) -> Unit)
/* the following is only here for Java */
@JvmDefault // this requires that you add -Xjvm-default=enable to your compiler flags!
fun bar(x:Consumer<String>) = bar(x::accept)
}
这有一些缺点:Consumer
方法在Kotlin中也是可见的,因此也可以从Kotlin中调用。不用说,您需要复制接口中的所有功能,因此整个Kotlin接口只会变得更加肿。但是:它在双方都可以按您期望的方式工作。 Java调用Consumer
-variant,Kotlin调用(String) -> Unit
-variant ...希望;-)实际上只是演示了一些调用:
// from Java:
..bar(s -> { System.out.println(s); })
// however, method references might not work that easily or not without a workaround...
..bar((Consumer<String>) System.out::println); // not nice... @JvmName("kotlinsBar") to the rescue? well... that will just get more and more ugly ;-)
// from Kotlin:
..bar(Consumer(::println)) // or: ..bar(Consumer { println(it) })
..bar(::println) // or: ..bar { println(it) } // whatever you prefer...
话虽如此,另一个变种是添加帮助程序方法,该方法实际上有助于从Java轻松调用Kotlin函数,例如如下:
fun <T> `$`(consumer: Consumer<T>): (T) -> Unit = consumer::accept
可能永远不会从Kotlin调用它(因为编写带$的反引号已经很麻烦了),或者如果您不想膨胀您的Kotlin代码,只需在Java中添加这样的方法即可,但实际上并没有看起来苗条:
static <T> Function1<T, Unit> $(Consumer<T> consumer) {
return t -> {
consumer.accept(t);
return Unit.INSTANCE;
};
}
对这些方法的调用都相同:
..bar($(s -> /* do something with s */)) // where bar(x : (String) -> Unit)
对于需要解决的问题,我只是返回了Unit.INSTANCE
或null
,但是如果我有更多的调用方法,我可能会选择第二种($(...)
)方法。在最佳情况下,我只需要提供(生成?;-))等效项并在多个项目中使用它们,而仅在Java接口中提供default
变体可能会需要更多的工作,甚至可能使某些人感到困惑...
最后:不...我不知道有任何选项可以让您在Kotlin的void
返回功能接口中拥有类似Unit
功能的接口(/消费者)
答案 1 :(得分:2)
实际上没有必要在 Roland 的回答中添加 $
符号。
只需一个方法名,即可在kotlin和java中直接获得良好的编码体验。
这是你需要做的(实际上和罗兰的回答一样,但我的示例代码供你参考):
interface `Consumer$`<T> : Function1<T, Unit> {
fun accept(t: T)
@JvmDefault
override fun invoke(t: T) {
accept(t)
}
}
// this interface is a FunctionalInterface
// the `$` prevents importing collision with java.util.functional.Consumer
class Foo {
fun bar(f: (String) -> Unit) {}
fun bar(f: `Consumer$`<String>) {
bar(f as (String)->Unit)
// because that the `Consumer$` extends Function1
// it can be directly passed as a kotlin function
}
}
foo.bar { it.xxx }
foo.bar(it -> it.xxx)
// as well as
foo.bar(System.out::println)
javac知道引用方法的返回类型不是Unit
,所以会调用Consumer$
方法。
kotlin 编译器不会检查 FunctionX
的继承,所以会调用 (String)->Unit
方法。