如何在 Kotlin 中将功能接口实现为 lambda?

How to implement a functional interface as lambda in Kotlin?

我想将功能性 kotlin 接口(具有单个抽象方法的接口)实现为 kotlin lambda。怎么做?

Kotlin 接口

@FunctionalInterface
interface Foo{
  fun bar(input: String): String 
}

Kotlin 实现 .

fun createFoo(): Foo {
   return { input: String -> "Hello $input!" }
}

↑ 不编译 ↑

它必须作为对象来实现,这非常丑陋。

fun createFoo() = 
     object : Foo{
            override fun bar(input: String)= "Hello $input"
     }

编辑:将我的示例界面从 java 更正为 kotlin

我认为没有语言级别的选项可以执行此操作,但您可以将 "ugly" 代码抽象为辅助方法,以便在实际需要业务逻辑的地方更容易阅读:

辅助方法

fun Foo(body: (String) -> String) = object : Foo{
  override fun bar(input: String)= body(input)
}

公司代码

fun createFoo():Foo {
  return Foo {input:String -> "Hello $input!"}
}

要使用 Kotlin lambda 到 Java SAM 接口转换任何你想要的地方,只需在 lambda 之前指定接口的名称。

fun createFoo(): Foo {
   return Foo { input:String -> "Hello $input!" }
}

甚至不需要那么长。

fun createFoo(): Foo {
   return Foo { "Hello $it!" }
}

只要接口只有一个方法并在 Java 中声明,就可以了。

在你的情况下,在 Java:

中使用界面会更容易
fun createFoo() : Foo = Foo { "hello $it" }

但是由于您使用的是 Kotlin 界面,所以您在这里有点不走运。这是一个与此相关的问题:KT-7770

解决此问题的方法可能是(但这主要取决于您如何使用该接口)在适当的位置设置如下 Kotlin 接口,这是 Java 端的主要入口点:

interface Foo : (String) -> String

在 Kotlin 方面你不会使用它,Java 方面应该只用它来交付功能,例如

// Java class
public class JFooFactory implements FooFactory {
  @Override
  @NotNull
  public Foo createFoo() { // uses the Foo-interface from Kotlin
    return input -> "hello " + input;
  }
}

// Kotlin equivalent:
class KFactory : FooFactory {
  override fun createFoo() : (String) -> String = {
    "hello $it"
  }
}

相应的 FooFactory 界面可能如下所示:

interface FooFactory {
  fun createFoo() : (String) -> String
}

用法可能如下所示:

listOf(KFooFactory(), JFooFactory())
     .map {
         it.createFoo()
     }
     .forEach { func : (String) -> String -> // i.e. func is of (sub)type (String) -> String
        func("demo") // calling it to deliver "hello demo" twice
     }

或者,要对 Kotlin 也有 Foo 的感觉,您可以按如下方式进行:

typealias Foo = (String) -> String
interface JFoo : Foo 
// or if you put the interface in its own package you could also use:   
interface Foo : someother.package.Foo

然后 Java 代码保持与上面相同,或者 JFooFoo 指向另一个包;类型别名在 Java 中不可见。 Kotlin 端将更改为以下内容:

class KFactory : FooFactory {
  override fun createFoo() : Foo = {
    "hello $it"
  }
}

Factory接口也可以被替换:

interface FooFactory {
  fun createFoo() : Foo
}

但在引擎盖下,一切都保持不变。我们在 Kotlin 中 have/use (String) -> String 和 Java.

中的 Foo-functional-interface

I want to implement a functional kotlin interface (interface with a single abstract method) as a kotlin lambda. How to do that?

不能

It has to be implemented as object, which is ugly as hell.

确实如此。


您有两个选择:

1.) 使用typealias

typealias Foo = (String) -> String

fun createFoo(): Foo = { "Hello $it!" }

2.) 根据您的 API,您可以定义一个扩展函数,它接收函数类型 (String) -> String 作为 crossinline 参数,然后在 [=14] 中调用它=] 块。这样,您可以在给定函数中隐藏 object:,并且在外部仍然可以使用 lambda 参数调用它。不过在这种情况下似乎不适用。

从 Kotlin v1.4 开始

1.4 版将支持 SAM 转换,并使用新的类型推理算法。

参见:


Kotlin v1.4 之前

如果伴生对象实现采用 lambda 的 invoke 函数,它就会起作用。

Kotlin 接口

interface Foo{
  fun bar(input: String): String

   companion object { 
      inline operator fun invoke(crossinline function: (String) -> String) =
                object : Foo{
                    override fun bar(input: String) = function(input)
                } 
   } 
}

Kotlin 实现

fun createFoo()= Foo { input: String -> "Hello $input!" }

Functional/SAM kotlin 中定义的接口在设计上不能作为 Kotlin lambda 实现,请参阅 KT-7770

在 Kotlin 中,函数式/SAM 接口被视为反模式,应该声明函数类型:(String)->String。函数类型可以表示为类型别名,使其看起来和感觉起来像一个接口:typealias Foo=(String)->String

注意:类型别名仅在 Kotlin 的 Java 代码中不可见!

只需将 fun 关键字添加到您的接口声明中:

fun interface Foo {
    fun bar(input: String): String 
}

是Kotlin函数式接口的注解(而不是Java中的@FunctionalInterface注解)。

现在你可以这样实现了:

Foo { input: String -> "Hello $input!" }

查看更多:https://kotlinlang.org/docs/fun-interfaces.html