Как реализовать функциональный интерфейс как лямбда в Kotlin? - PullRequest
1 голос
/ 05 ноября 2019

Я хочу реализовать функциональный интерфейс kotlin (интерфейс с одним абстрактным методом) в виде kotlin lambda. Как это сделать?

Интерфейс Kotlin

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

Реализация Kotlin .

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

↑ неcompile ↑

Он должен быть реализован как объект, который ужасен как ад.

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

РЕДАКТИРОВАТЬ: исправил мой пример интерфейса от Java до kotlin

Ответы [ 5 ]

2 голосов
/ 05 ноября 2019

Я хочу реализовать функциональный интерфейс kotlin (интерфейс с одним абстрактным методом) в виде kotlin lambda. Как это сделать?

Не могу

Он должен быть реализован как объект, который ужасен как ад.

Действительно.


У вас есть два варианта:

1.) Использовать typealias

typealias Foo = (String) -> String

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

2.) В зависимости от вашего API вы можете определить расширениефункция, которая получает функциональный тип (String) -> String в качестве аргумента crossinline, а затем вызывает его внутри блока object: __. Таким образом, вы можете скрыть object: в данной функции и по-прежнему иметь возможность вызывать ее с лямбда-аргументом. Однако в данном случае это не представляется возможным.

1 голос
/ 05 ноября 2019

В вашем случае было бы проще иметь интерфейс в 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
     }

В качестве альтернативы, чтобы также иметь это Foo -чувство для Kotlin васможет сделать это следующим образом:

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 останется таким же, как указано выше, либо с JFoo или с Foo, указывающим на этот другой пакет;typealias не виден из Java. Сторона Kotlin изменится на следующее:

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

Интерфейс Factory также можно заменить:

interface FooFactory {
  fun createFoo() : Foo
}

Под капотом, однако, все остается прежним. У нас есть / 10 * * в Kotlin и Foo -функциональный интерфейс в Java.

1 голос
/ 05 ноября 2019

Я не думаю, что для этого есть опция на уровне языка, но вы можете абстрагировать «некрасивый» код во вспомогательный метод, чтобы его было легче читать там, где на самом деле нужна бизнес-логика:

вспомогательный метод

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

код предприятия

fun createFoo():Foo {
  return Foo {input:String -> "Hello $input!"}
}
0 голосов
/ 06 ноября 2019

Работает, если объект-компаньон реализует функцию 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!" }

Функциональные / SAM-интерфейсы, определенные в kotlin, не могут быть реализованы как лямбды Kotlin, см. KT-7770 .

В Kotlin функциональный интерфейс / интерфейс SAM рассматривается как анти-шаблон, вместо него должен быть объявлен тип функции: (String)->String. Тип функции может быть выражен как typealias, чтобы он выглядел и чувствовал себя как интерфейс: typealias Foo=(String)->String. Примечание: typealias не виден в коде Java только в Kotlin!

0 голосов
/ 05 ноября 2019

Чтобы использовать преобразование Kotlin лямбда в Java SAM где угодно, просто укажите имя интерфейса перед лямбда.

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

Это даже не обязательно должно быть так долго.

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

Пока интерфейс имеет единственный метод и объявлен в Java, это все, что вам нужно сделать.

...