[英]How to declare a Kotlin Lambda with return type 'void' for a java caller?
I have a library that is completely written in Kotlin including its public API.我有一个完全用 Kotlin 编写的库,包括它的公共 API。 Now a user of the library uses Java, the problem here is that Kotlin Lambdas with return type
Unit
are not compiled to return type void
.现在该库的用户使用 Java,这里的问题是具有返回类型
Unit
的 Kotlin Lambda 未编译为返回类型void
。 The effect is that the Java side has always to return Unit.INSTANCE
for methods that are effectivly void
.结果是 Java 端必须始终为有效
void
的方法返回Unit.INSTANCE
。 Can this be avoided somehow?这可以以某种方式避免吗?
Example:例子:
Kotlin Lambda科特林拉姆达
interface Foo{
fun bar(x:(String)->Unit)
}
Java call 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
}
Is it possible to declare the Kotlin Lambda differently so the compiler generates a void
return type?是否可以以不同方式声明 Kotlin Lambda,以便编译器生成
void
返回类型?
see also How to declare a Kotlin function with return type 'void' for a java caller?另请参阅如何为 Java 调用者声明返回类型为“void”的 Kotlin 函数?
If Java interop is of topmost priority I would either just use the Java functional interfaces directly (ie Consumer
, Supplier
, etc.) or create custom Kotlin functional interfaces for now.如果 Java 互操作是最优先考虑的,我要么直接使用 Java 功能接口(即
Consumer
、 Supplier
等),要么暂时创建自定义Kotlin 功能接口。 Meanwhile Kotlin handles functional interfaces better...同时 Kotlin 更好地处理功能接口......
Java variant: Java变体:
interface Foo{
fun bar(x : java.util.function.Consumer<String>)
}
// calling this from Kotlin today looks the same as if we used (String) -> Unit:
foo.bar { println(it) }
Kotlin variant with custom Consumer
:具有自定义
Consumer
的 Kotlin 变体:
fun interface MyConsumer<T> { // just a demo... probably depends on your needs
fun accept(t : T)
// other functions?
}
Usage is similar as above.用法和上面类似。 Maybe there is even an easier way today to handle something like
(String) -> Unit
as Consumer<String>
but then I do not know it yet (or didn't feel the need to research for it yet;-)).也许今天甚至有一种更简单的方法来处理诸如
(String) -> Unit
as Consumer<String>
之类的东西,但后来我还不知道(或者觉得还没有必要研究它;-))。 Compiler annotations as Ilya mentioned in the comments may be a way to solve this issue ~centrally. Ilya 在评论中提到的编译器注释可能是集中解决此问题的一种方法。
In Dec 2018 I wrote: I do not have a real answer to this, but I will share, what I did in such a situation where I needed to access such Kotlin code from Java (or what has come to my mind).在 2018 年 12 月,我写道:我对此没有真正的答案,但我会分享我在需要从 Java 访问此类 Kotlin 代码的情况下所做的事情(或者我想到的)。
Basically it depends which side you really want to touch/enhance just to get what you require.基本上,这取决于您真正想要触摸/增强哪一侧以获得您需要的东西。
Enhancing the Kotlin code to support the Java equivalents:增强 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)
}
This has some drawbacks: the Consumer
-method is visible from Kotlin as well and is therefore also callable from there.这有一些缺点:
Consumer
方法在 Kotlin 中也是可见的,因此也可以从那里调用。 Needless to say, that you need to duplicate all the functions in the interfaces and therefore your whole Kotlin-interfaces just get more bloated.不用说,您需要复制接口中的所有功能,因此您的整个 Kotlin 接口只会变得更加臃肿。 But: it works from both sides the way you would expect.
但是:它的工作方式与您期望的一样。 Java calls the
Consumer
-variant, Kotlin calls the (String) -> Unit
-variant... hopefully;-) actually just demoing some calls: 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...
That having said, another variant is to add helper methods that actually help calling the Kotlin functions easier from Java, eg something as follows:话虽如此,另一种变体是添加辅助方法,这些方法实际上有助于更轻松地从 Java 调用 Kotlin 函数,例如:
fun <T> `$`(consumer: Consumer<T>): (T) -> Unit = consumer::accept
Which will probably never be called from Kotlin (as writing the backticks combined with the $ is already cumbersome enough) or if you do not want to bloat your Kotlin code just add such a method to Java, where however it doesn't look that slim:这可能永远不会从 Kotlin 调用(因为将反引号与 $ 结合起来已经很麻烦了)或者如果你不想膨胀你的 Kotlin 代码只需将这样的方法添加到 Java,但是它看起来并不那么苗条:
static <T> Function1<T, Unit> $(Consumer<T> consumer) {
return t -> {
consumer.accept(t);
return Unit.INSTANCE;
};
}
Calls to those methods both look the same:对这些方法的调用看起来都一样:
..bar($(s -> /* do something with s */)) // where bar(x : (String) -> Unit)
For the things I needed to solve I just returned Unit.INSTANCE
or null
, but if I had more methods to call I would probably have chosen the second ( $(...)
) approach.对于我需要解决的问题,我只是返回
Unit.INSTANCE
或null
,但如果我有更多方法可以调用,我可能会选择第二种 ( $(...)
) 方法。 In the best case I only need to supply (generate? ;-)) equivalents once and use them in several projects whereas supplying default
variants in the interfaces just for Java will probably require way more work and may even confuse some people...在最好的情况下,我只需要提供 (generate? ;-)) 等价物一次并在多个项目中使用它们,而在接口中为 Java 提供
default
变体可能需要更多的工作,甚至可能会让一些人感到困惑......
Finally: no... I don't know of any option that allows you to have something like void
-functional interfaces (/consumers) out of the Unit
-returning functional interfaces of Kotlin.最后:不......我不知道有什么选项可以让你从 Kotlin 的
Unit
返回功能接口中获得类似void
功能接口(/消费者)的东西。
Actually there's no need to add the $
symbol in Roland's answer.其实罗兰的回答中不需要加
$
符号。
You can directly achieve good coding experience in both kotlin and in java with only one method name.只需一个方法名,就可以直接在kotlin和java中获得良好的编码体验。
Here's what you need to do (actually it's the same as Roland's answer but with my example code for you to refer to):这是您需要做的(实际上它与罗兰的答案相同,但带有我的示例代码供您参考):
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)
The javac knows that the return type of the referencing method is not Unit
, so it will call the Consumer$
method. javac 知道引用方法的返回类型不是
Unit
,因此它将调用Consumer$
方法。
The kotlin compiler will not check the inheritance of FunctionX
, so it will call the (String)->Unit
method. kotlin 编译器不会检查
FunctionX
的继承,因此它会调用(String)->Unit
方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.