Cannot replace SAM constructor with lambda when the first argument is a single method class

I am puzzled by SAM constructors, I have this Java class:

public class TestSam<T> { public void observe(ZeroMethods zero, Observer<T> observer) { } public void observe(OneMethod one, Observer<T> observer) { } public void observe(TwoMethods two, Observer<T> observer) { } public interface Observer<T> { void onChanged(@Nullable T t); } public interface ZeroMethods { } public interface OneMethod { First getFirst(); } public interface TwoMethods { First getFirst(); Second getSecond(); } public interface First { } public interface Second { } } 

And this Kotlin code:

 fun testSam( test: TestSam<String>, zero: TestSam.ZeroMethods, one: TestSam.OneMethod, two: TestSam.TwoMethods ) { test.observe(zero) { println("onChanged $it") } // 1. compiles test.observe(zero, TestSam.Observer { println("onChanged $it") }) // 2. Redundant SAM-constructor test.observe(one) { println("onChanged $it") } // 3. doesn't compile test.observe({ one.first }) { println("onChanged $it") } // 4. compiles test.observe(one, TestSam.Observer { println("onChanged $it") }) // 5. compiles test.observe(two) { println("onChanged $it") } // 6. compiles test.observe(two, TestSam.Observer { println("onChanged $it") }) // 7. Redundant SAM-constructor } 

What is the deal here? Why can't Kotlin determine 3. (and provide special option 4.), but handle all the other cases?


The rationale for this code is the LiveData<T>.observe(LifecycleOwner owner, Observer<T> observer) method on Android, where LifecycleOwner has one getLifecycle() method.

+7
java android interop kotlin android-architecture-components
source share
2 answers

With the upcoming new type output, this problem will be fixed in the Kotlin compiler. Experimental type inference can now be enabled (Kotlin 1.3 required) in an Android project by adding it to the module level Gradle file:

 tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { kotlinOptions { freeCompilerArgs = ["-XXLanguage:+NewInference"] } } 

It is assumed that it is possible to enable it simply (but not yet completely ):

 kotlin { experimental { newInference = "enable" } } 

The seer wrote:

The reason for this is the technical complexity of compiler design.

In my opinion, the Kotlin compiler does not want to generate 2^n options for a method that has n parameters suitable for SAM conversion, so instead it only generates two options: one where all are lambdas and one where there are no lambdas

YouTrack has a related problem: it is impossible to pass not all SAM arguments as a function

0
source share

I found a rule in the compiler: if the Java method requires types that are SAM interfaces, then you can replace them with lambdas (or functions) , but either all such parameters, or none of them.

So, you have a method: public void observe(OneMethod one, Observer<T> observer) . Both parameters are SAM candidates. You may call:
observer(object1, object2)
or:
observer(function1, function2)

but not :
observer(object1, function2)
and not :
observer(function1, object2)

The same behavior will occur even in the case of 3 or more parameters. The reason for this is technical difficulties in compiler design.

Sorry if I'm not very clear, I'm not very good in English.

+2
source share

All Articles