I want to pass a property getter method reference as one of the function arguments, and have that argument be of my own functional interface type, but ran into an issue.
Here's a stripped down minimal reproducible case, I changed the variable from an argument into a property, but the issue is the same.
class Foo {
val bar: Bar? = null
}
class Bar
fun interface FooBarSelector {
fun select(foo: Foo): Bar?
}
class KotlinClass() {
val selector: FooBarSelector = Foo::bar
}
This doesn't compile, Foo::bar
is underlined and the error is
Type mismatch.
Required: FooBarSelector
Found: KProperty1<Foo, Bar?>
I tried to look this up, found similar questions about SAM conversions, but they were a bit different and I don't think any of them referred specifically to property getters.
I found that the issue can be solved by doing one of the following:
- Remove the explicit type, or replace it with the suggested
KProperty1
. Not an option, I want to preserve my type. - Replace the method reference with
FooBarSelector { it.bar }
. Far from ideal, but better than the first option.
Why does this happen and are there any other options? I am new to Kotlin, but not Java.
Kotlin version used is 1.7.20
EDIT:
Here's my original goal: accept a FooBarSelector
as an argument, and by default point it at a property getter:
fun doSomething(
selector: FooBarSelector = Foo::bar //doesn't compile
) {
}
CodePudding user response:
Your code with the lambda is fine, but you may prefer this syntax for the same thing:
class KotlinClass() {
val selector = FooBarSelector(Foo::bar)
}
Explanation:
Kotlin function references are more explicit about types than in Java, because function references are a first-class object type. When you want to use a function reference as a functional interface instance, you must convert it. This can be done automatically by the compiler using SAM conversion.
SAM conversion only works when passing a function reference as an argument to a function that has a parameter with a functional interface type. So, it doesn't directly work when assigning to a property.
But Kotlin implicitly provides higher order functions for functional interfaces that allow you to pass a function reference that will convert it into an interface instance. The implicit function is named after the interface, so it looks like a constructor call.
In the above code, the implicit functional interface "constructor" is inline
, so there is no intermediate functional object allocated in the compiled code. This compiles to the same thing you would get in Java with a direct method reference.
CodePudding user response:
I am not 100% sure what you are expecting but consider this example:
class Foo(
val bar: Bar
)
data class Bar(
val value: String
)
interface FooBarSelector {
fun select(foo: Foo): Bar {
return foo.bar
}
}
class FooBarCustomSelector: FooBarSelector {
override fun select(foo: Foo): Bar {
return Bar("I don't care about which Foo was passed. I'll return my own Bar")
}
}
class KotlinClass(val selector: (Foo) -> Bar = Foo::bar)
fun main(args: Array<String>) {
val kotlinClassWithDefaultSelector = KotlinClass()
val kotlinClassWithCustomSelector = KotlinClass(FooBarCustomSelector()::select)
val foo = Foo(Bar("Bar1"))
println("kotlinClassWithDefaultSelector: ${kotlinClassWithDefaultSelector.selector(foo)}")
println("kotlinClassWithCustomSelector: ${kotlinClassWithCustomSelector.selector(foo)}")
}
This would print:
kotlinClassWithDefaultSelector: Bar(value=Bar1)
kotlinClassWithCustomSelector: Bar(value=I don't care about which Foo was passed. I'll return my own Bar)