Search code examples
lambdakotlinfunctional-interface

How to use functional-interface inside .kt class


I would like to use some of functional-interfaces inside the java.util.function package like DoubleBinaryOperator interface.

I can use it in Java as below:

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

}

But it doesn't work for me in Kotlin because Kotlin cannot infer the type of the parameter.

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

}

Solution

  • You can achieve what you want (SAM conversion) with the following syntax:

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

    Note that this works only to implement Java interfaces, as documented here.

     EDIT

    To expand on my comment below, Kotlin lambdas can be used in place of functional interfaces (what is known as SAM conversion) only when calling Java code from Kotlin. This is not allowed in pure Kotlin as you can use function types (e.g., (Double, Double) -> Double, to mimic DoubleBinaryOperator).

    As an example, consider the following Java class:

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

    In Kotlin you can use it like this:

    val myClass = MyClass()
    
    // Use case 1
    myClass.append("something") {
        "suffix"
    }
    
    // Use case 2
    myClass.append("Something", Supplier { "suffix" })
    

    Note that my IDE tells me there's a "Redundant SAM-constructor" for use case 2.

    Now, let's rewrite MyClass in Kotlin:

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

    If we don't change the code using it, we'll get a compilation error for use case 1: "Required Supplier, found () -> String" (that's the same issue you're getting) because SAM conversion cannot be done. However, you can "force" it by using SAM constructors (that is use case 2).