Как использовать функциональный интерфейс внутри класса .kt

#lambda #kotlin #functional-interface

#лямбда #kotlin #функциональный интерфейс

Вопрос:

Я хотел бы использовать некоторые функциональные интерфейсы внутри java.util.function пакета, подобные DoubleBinaryOperator interface.

Я могу использовать его в Java, как показано ниже:

 public enum Operation {
    PLUS(" ", Double::sum),
    MINUS("-", (x, y) -> x - y),
    TIMES("*", (x, y) -> x * y),
    DIVIDE("/", (x, y) -> x / y);

    private final String symbol;
    private final DoubleBinaryOperator op;

    Operation(String symbol, DoubleBinaryOperator op) {
        this.symbol = symbol;
        this.op = op;
    }

}
  

Но у меня это не работает в Kotlin, потому что Kotlin не может определить тип параметра.

 enum class Operation private constructor(private val symbol: String, private val op: DoubleBinaryOperator) {
    PLUS(" ", { x, y -> x   y }),
    MINUS("-", { x, y -> x - y }),
    TIMES("*", { x, y -> x * y }),
    DIVIDE("/", { x, y -> x / y });

}
  

Ответ №1:

Вы можете достичь желаемого (преобразование SAM) с помощью следующего синтаксиса:

 enum class Operation private constructor(private val symbol: String, private val op: DoubleBinaryOperator) {
    PLUS(" ", DoubleBinaryOperator { left, right -> left   right }),
    ...
}
  

Обратите внимание, что это работает только для реализации интерфейсов Java, как описано здесь.

Редактировать

Чтобы расширить мой комментарий ниже, лямбды Kotlin могут использоваться вместо функциональных интерфейсов (то, что известно как преобразование SAM) только при вызове Java-кода из Kotlin. Это не разрешено в чистом Kotlin, поскольку вы можете использовать типы функций (например, (Double, Double) -> Double для имитации DoubleBinaryOperator ).

В качестве примера рассмотрим следующий класс Java:

 public class MyClass {
    String append(String input, Supplier<String> supplier) {
        return input.concat(supplier.get());
    }
}
  

В Kotlin вы можете использовать его следующим образом:

 val myClass = MyClass()

// Use case 1
myClass.append("something") {
    "suffix"
}

// Use case 2
myClass.append("Something", Supplier { "suffix" })
  

Обратите внимание, что моя среда разработки сообщает мне, что для варианта использования 2 существует «Избыточный SAM-конструктор».

Теперь давайте перепишем MyClass на Kotlin:

 class MyClass {
    fun append(input: String, supplier: Supplier<String>): String {
        return input   supplier.get()
    }
}
  

Если мы не изменим код, используя его, мы получим ошибку компиляции для варианта использования 1: «Требуемый поставщик, найден () -> Строка» (это та же проблема, которую вы получаете), потому что преобразование SAM не может быть выполнено. Однако вы можете «принудительно» использовать его с помощью конструкторов SAM (это вариант использования 2).

Комментарии:

1. Спасибо, но почему мне нужно писать интерфейс явно?

2. Проблема в том, что преобразование SAM (с использованием лямбд, где ожидается функциональный интерфейс) работает только при вызове Java-кода из Kotlin. В вашем случае вы определяете Operation в Kotlin, поэтому преобразование SAM не работает, и вам нужно явно написать тип (который называется SAM constructor). Ссылка в моем ответе также объясняет, почему преобразование SAM не работает в таких случаях

3. @IbrahimAli Я обновил свой ответ, добавив больше деталей, надеюсь, теперь это понятнее