有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

如何为java调用程序声明返回类型为“void”的Kotlin Lambda?

我有一个完全用Kotlin编写的库,包括它的公共API。现在库的用户使用Java,这里的问题是返回类型为Unit的Kotlin lambda没有编译为返回类型void。其效果是,对于有效地void的方法,Java端始终必须返回Unit.INSTANCE。这能以某种方式避免吗

例如:

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?


共 (2) 个答案

  1. # 1 楼答案

    如果Java互操作是最重要的,我要么直接使用Java功能接口(即ConsumerSupplier,等等),要么现在就创建自定义Kotlin functional interfaces。同时Kotlin更好地处理功能接口

    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) }
    

    带有自定义Consumer的Kotlin变体:

    fun interface MyConsumer<T> { // just a demo... probably depends on your needs
       fun accept(t : T)
       // other functions?
    }
    

    用法同上。也许今天有一种更简单的方法来处理像(String) -> Unit这样的事情,但是我还不知道(或者觉得还没有必要研究它;-)。Ilya在评论中提到的编译器注释可能是解决这个问题的一种方法

    2018年12月,我写道:我没有一个真正的答案,但我将分享我在这种情况下做了什么,我需要从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接口变得更加臃肿。但是:它从两个方面都起作用,就像你期望的那样。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调用(因为将backticks与$组合在一起编写已经够麻烦了),或者如果您不想让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.INSTANCEnull,但是如果我有更多的方法可以调用,我可能会选择第二种($(...))方法。在最好的情况下,我只需要提供(生成?;-))等价物一次,并在几个项目中使用它们,而仅为Java提供接口中的default变体可能需要更多的工作,甚至可能会让一些人感到困惑

    最后:不。。。我不知道有哪个选项可以让你在Kotlin的Unit返回的功能接口中有void这样的功能接口(/consumers)

  2. # 2 楼答案

    实际上,没有必要在罗兰的答案中添加$符号
    只需一个方法名,就可以直接在kotlin和java中获得良好的编码体验

    以下是您需要做的事情(实际上与罗兰的答案相同,但我的示例代码供您参考):

    1. 定义一个供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
    
    1. 在类/接口中,添加一个供java调用的函数
    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
        }
    }
    
    1. 在kotlin代码中,您可以编写
    foo.bar { it.xxx }
    
    1. 在java代码中,您可以编写
    foo.bar(it -> it.xxx)
    // as well as
    foo.bar(System.out::println)
    

    javac知道引用方法的返回类型不是Unit,因此它将调用Consumer$方法
    kotlin编译器不会检查FunctionX的继承,因此它将调用(String)->Unit方法