Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't replace SAM-constructor with lambda when first argument is a class with one method

I’m puzzled over 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's the deal here? Why can't Kotlin figure out 3. (and provides the special variant 4.), but handles all other cases?


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

like image 808
arekolek Avatar asked Oct 03 '17 10:10

arekolek


1 Answers

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

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

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

Same behaviour will be even in case of 3 or more parameters. The cause of this is technical difficulty in compiler design.

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

like image 154
Beholder Avatar answered Oct 20 '22 17:10

Beholder