Kotlin SAM interface and method reference - kotlin

I am a bit confused why I can do the following
val outputter1: (s: String) -> Unit = ::println
but when I do
val outputter2: Outputter = ::println
given
fun interface Outputter {
fun output(output: String)
}
I get a compilation error
None of the following functions can be called with the arguments supplied.
println() defined in kotlin.io
println(Any?) defined in kotlin.io
...
Shouldn't method references translate to SAM inteface types as well as Function types?

Apparently, SAM conversions must be explicit in assignments (despite the type declaration) using the auto-generated adapter functions:
val outputter2: Outputter = Outputter { println(it) } // OK
val outputter3: Outputter = Outputter(::println) // OK
val outputter4: Outputter = { s: String -> println(s) } // compile error
But they are inferred fine in function calls:
fun main() {
takesOutputter(::println) // OK
}
fun takesOutputter(o: Outputter) {
o.output("test")
}
For reference, checkout the doc on Kotlin SAM conversions, and this part in the Java interop section which gives more examples of SAM conversions.

Related

Kotlin DSL - type inference on returned value

I'm trying to introduce the following (simplified) DSL:
fun <T> myDsl(specFn: DslSpec<T>.() -> Unit) {
val value = DslSpec<T>().apply(specFn).fn!!()
println("value is: $value")
}
class DslSpec<T> {
internal var fn: (() -> T)? = null
fun getValue(fn: () -> T) {
this.fn = fn
}
}
fun testCase() {
myDsl {
getValue {
"abc"
}
}
}
But it fails to infer T based just on the returned type of getValue ("Not enough information to infer type variable T"). I kind of see how it could be a very hard task to do for a compiler, but thought maybe there are already some tricks to make constructs like this work?
If you're using a version of Kotlin < 1.6.0, you should add #BuilderInference to the specFn argument:
fun <T> myDsl(#BuilderInference specFn: DslSpec<T>.() -> Unit) {
...
}
https://pl.kotl.in/__xy04j88
If you're using a version >= 1.6.0, you should either use the annotation as well, or both your declarations and their usages must be compiled with the compiler argument -Xenable-builder-inference.

How to force Kotlin to call a particular overloaded method in Java interface?

There is a Java interface
interface MyContract {
<M> void execute(Class<M> argClass, Consumer<M> action);
<M, R> R execute(Class<M> argClass, Function<M, R> action);
}
When execute method is called from Kotlin code by default compiler always uses the first overloaded method, even when the expected type was explicitly set, there is a compilation error:
MyContract myContract = createSomehow();
val x: Int = myContract.execute(SomeClass::class.java, { it -> 1})
Compilation error:
Type mismatch: inferred type is Unit but Int was expected
To force compiler use the second overloaded method I add this boilerplate:
val fn: (SomeClass) -> Int = { it -> 1 }
val x: Int = myContract.execute(SomeClass::class.java, fn)
What is a normal syntactical way to express intention to call a particular overloaded method in this case?
This problem is not java-interop specific, if a similar interface was defined in Kotlin, the error would be the same. Looks like there is room for an overload resolution mechanism enhancement.
For a workaround you may use the fact, that overloaded methods have a different amount of type parameters, so you may specify them explicitly:
val x = myContract.execute<SomeClass, Int>(SomeClass::class.java) { it -> 1 }
This is still noisy, so I'd suggest declaring Kotlin-specific API:
internal inline fun <reified M> MyContract.execute(noinline action: ((M) -> Unit)?) = execute(M::class.java, action)
internal inline fun <reified M, R> MyContract.execute(noinline action: ((M) -> R)?) = execute(M::class.java, action)
Now it may be called with:
val x = myContract.execute<SomeClass, Int> { it -> 1 }

Unexpected behavior of also/apply, cannot pass references of a instance function into also/apply

To sum up the question in a few words, here is the catch:
The also(strings::add) doesn't work, it says Type inference failed
fun test() = "Test String"
val strings = mutableListOf<String>()
// Type inference failed: inline fun <T> T.also(block: (T) -> Unit): T cannot be applied to receiver: String arguments: (<unknown>)
// None of the following functions can be called with the arguments supplied: public abstract fun add(index: Int, element: String): Unit defined in kotlin.collections.MutableList public abstract fun add(element: String): Boolean defined in kotlin.collections.MutableList
test().also(strings::add).let { /* Use the string later */ }
While doing the same with let does work in the same place:
val strings = mutableListOf<String>()
test().let(strings::add).let { println(it) } // prints true, no compile errors.
Here is the minimal reproducable code.
I want to use the string later so don't want to use let here. What should I do? If i try to use the apply the same compile error occur probably because both also and apply have same callback signature of KFunction1<T, T>. How should one pass these type of references with also/apply?
override fun add(element: E): Boolean as you can see, the function returns Boolean, but apply accepts block: T.() -> Unit, i.e. it accepts only functions that receive a single argument and return no value.

Simple SAM needs conversion to anonymous object

I have the following simple interface
interface A {
fun move(s:Boolean): Int
}
I have the following class
class X{
fun draw (x: A): String{
return "A"
}
fun main() {
val temp = A {
s -> 100
}
val a = draw ( { x -> 100} )
}
}
However both temp and a fail to be declared. temp complains and the suggested fix is to convert to an anonymous object as follows (which defeats the whole purpose of using a SAM?)
val temp = object : A {
override fun move(s: Boolean): Int {
return 100
}
}
a complains about a type mismatch. My question is why does this simple SAM fail? The method signature is the same.
Currently Kotlin only has SAM conversion for interfaces defined in Java.
For pure Kotlin code you're supposed to use a function type such as:
typealias A = (s: Boolean) -> Int
However, the syntax you expected will be supported in Kotlin 1.4 with interface defined as fun interface.
As announced here:
What to Expect in Kotlin 1.4 and Beyond
and tracked as KT-7770
Kotlin 1.4 has support for SAM conversions for Kotlin interfaces, in addition to the Java interface SAM conversion available in previous versions. See:
https://youtrack.jetbrains.com/issue/KT-7770
https://blog.jetbrains.com/kotlin/2019/12/what-to-expect-in-kotlin-1-4-and-beyond/#language-features
With 1.4, you do need to define your interface as a "functional" interface:
fun interface A {
fun move(s:Boolean): Int
}

How to implement a functional interface as lambda in Kotlin?

I want to implement a functional kotlin interface (interface with a single abstract method) as a kotlin lambda. How to do that?
Kotlin interface
#FunctionalInterface
interface Foo{
fun bar(input: String): String
}
Kotlin implementation .
fun createFoo(): Foo {
return { input: String -> "Hello $input!" }
}
↑ doesn't compile ↑
It has to be implemented as object, which is ugly as hell.
fun createFoo() =
object : Foo{
override fun bar(input: String)= "Hello $input"
}
EDIT: corrected my sample interface from java to kotlin
Just add the fun keyword to your interface declaration:
fun interface Foo {
fun bar(input: String): String
}
It is the notation of functional interfaces in Kotlin (instead of #FunctionalInterface annotation in Java).
Now you can implement it like this:
Foo { input: String -> "Hello $input!" }
See more: https://kotlinlang.org/docs/fun-interfaces.html
since Kotlin v1.4
SAM conversion will be supported with version 1.4, with a new type inference algorithm.
See:
What to Expect in Kotlin 1.4 and Beyond
More powerful type inference algorithm
before Kotlin v1.4
It works if the companion object implements the invoke function taking a lambda.
Kotlin interface
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 implementation
fun createFoo()= Foo { input: String -> "Hello $input!" }
Functional/SAM interfaces defined in kotlin can't be implemented as Kotlin lambdas by design, see KT-7770.
In Kotlin an functional / SAM interface is considered as an anti-pattern, a function type should be declared instead: (String)->String. The function type can be expressed as typealias to make it look and feel like an interface: typealias Foo=(String)->String.
Note: The typealias is not visible in Java code only in Kotlin!
I want to implement a functional kotlin interface (interface with a single abstract method) as a kotlin lambda. How to do that?
Can't
It has to be implemented as object, which is ugly as hell.
Indeed.
You have two options:
1.) use typealias
typealias Foo = (String) -> String
fun createFoo(): Foo = { "Hello $it!" }
2.) depending on your API, you can define an extension function that receives the functional type (String) -> String as a crossinline argument, then invokes it inside a object: __ block. That way, you can hide the object: in a given function, and externally still be able to call it with a lambda argument. Doesn't seem applicable in this case, though.
I don't think there is a language level option to do this, but you can abstract the "ugly" code into a helper method so its easier to read where the business logic is actually needed:
Helper Method
fun Foo(body: (String) -> String) = object : Foo{
override fun bar(input: String)= body(input)
}
Business Code
fun createFoo():Foo {
return Foo {input:String -> "Hello $input!"}
}
It would be easier in your case to have the interface in Java:
fun createFoo() : Foo = Foo { "hello $it" }
But as you have a Kotlin interface instead, you are a bit out of luck here. Here is a related issue regarding this: KT-7770
A workaround to this could be (but that mainly depends on how you use that interface) to have a Kotlin interface as follows in place that is the main entry point for the Java side:
interface Foo : (String) -> String
On the Kotlin side you will not use it and the Java side should only use it to deliver functions, e.g.
// 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"
}
}
where the corresponding FooFactory-interface could look like:
interface FooFactory {
fun createFoo() : (String) -> String
}
Usage could look like:
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
}
Alternatively, to have also that Foo-feeling for Kotlin you can do it as follows:
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
then the Java code stays the same as above, either with JFoo or with Foo pointing to that other package; the typealias is not visible from Java. The Kotlin side would change to the following:
class KFactory : FooFactory {
override fun createFoo() : Foo = {
"hello $it"
}
}
The Factory-interface could also be replaced:
interface FooFactory {
fun createFoo() : Foo
}
Under the hood however everything stays the same. We have/use (String) -> String in Kotlin and Foo-functional-interface in Java.
To use Kotlin lambda to Java SAM interface conversion anywhere you want, simply specify the name of the interface before the lambda.
fun createFoo(): Foo {
return Foo { input:String -> "Hello $input!" }
}
It doesn't even need to be that long.
fun createFoo(): Foo {
return Foo { "Hello $it!" }
}
As long as the interface has a single method and is declared in Java, that's all you need to do.