14
votes

I'm facing a compile error when trying to use lambdas / function references with kotlin:

class Foo {

    fun getFilteredList(){
        val numbers = listOf(1, 2, 3)
        numbers.filter(::isOdd) // prints [1, 3]
    }

    fun isOdd(x: Int): Boolean = x % 2 != 0

}

But I get a compile time error saying a type mismatch:

Error:(18, 16) Gradle: Type inference failed: inline fun kotlin.Iterable.filter(predicate: (T) -> kotlin.Boolean): kotlin.List cannot be applied to receiver: kotlin.List arguments: (kotlin.reflect.KFunction2) Error:(18, 23) Gradle: Type mismatch: inferred type is kotlin.reflect.KFunction2 but (kotlin.Int) -> ??? was expected Error:(18, 23) Gradle: Type mismatch: inferred type is kotlin.reflect.KFunction2 but (kotlin.Int) -> kotlin.Boolean was expected Error:(18, 25) Gradle: Left-hand side of a callable reference with a receiver parameter cannot be empty. Please specify the type of the receiver before '::' explicitly

I'm not sure what the error is nor what type I should specify explicitly before '::'

Another question: Can I use another objects function as reference in kotlin? Something like this:

class Bar {
    fun isOdd(x: Int): Boolean = x % 2 != 0
}

class Foo {

    fun getFilteredList(){
        val bar = Bar()
        val numbers = listOf(1, 2, 3)
        numbers.filter(bar::isOdd) // Use Bar's method
    }
}
2

2 Answers

18
votes

On the second example: yes, the bound function reference syntax is supported since Kotlin 1.1, so you can write bar::isOdd similarly to Java.

In the first example, the error is trying to say that isOdd is in fact a function of two parameters (of types Foo and Int), and passing a function taking two parameters as an argument whose type is function of one parameter is not allowed. To make the example compile, you can make isOdd a top-level or a local function, which would make it a function of one parameter of type Int. Or, if you use Kotlin 1.1+, use the bound function reference syntax and simply write this::isOdd.

7
votes

That's funny. "Java strikes back". Haha

Your problem is simple: you declared isOdd in class Foo, right? Then it is not a function, but a method. Which means that it requires an instance of Foo to be passed in (the this reference) - that's why it is a function of 2 parameters: Foo.(Int) -> Boolean. And the syntax error shows that - a reference to a method looks like Foo::isOdd.

Anyway, declaring a non-static method which does not use the object is an antipattern even in Java, don't you agree?

The problem might be solved by declaring a free function without a class or by making it an extension: fun Int.isOdd()

P.S. Regarding your second question - that feature is not supported yet.