当第一个参数是具有一个方法的类时,无法用lambda替换SAM构造函数

时间:2017-10-03 10:04:36

标签: java android kotlin interop android-architecture-components

我对SAM构造函数感到困惑,我有这个Java类:

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 {
    }
}

这个Kotlin代码:

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
}

这是什么交易?为什么Kotlin不能弄明白3.(并提供特殊的变体4.),但处理所有其他情况?

此代码的基本原理是Android中的LiveData<T>.observe(LifecycleOwner owner, Observer<T> observer)方法,其中LifecycleOwner有一种方法getLifecycle()

2 个答案:

答案 0 :(得分:2)

我在编译器中找到了一条规则:如果Java方法调用需要SAM-interfaces类型,那么你可以用lambdas(或函数)替换它们, all 这些参数,或没有

所以,你有方法:public void observe(OneMethod one, Observer<T> observer)。 两个参数都是SAM候选者。你可以打电话:
observer(object1, object2)
或:
observer(function1, function2)


observer(object1, function2)

observer(function1, object2)

即使在3个或更多参数的情况下也会出现相同的行为。 造成这种情况的原因是编译器设计中的技术难度。

对不起,如果我不是很清楚,我的英语不是很好。

答案 1 :(得分:1)

随着即将推出的新类型推断,这个问题将在Kotlin编译器中修复。现在可以在Android项目中启用实验类型推断(需要Kotlin 1.3),方法是将其添加到模块级gradle文件中:

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

应该可以使用(但it doesn't quite work yet):

来启用它
kotlin {
    experimental {
        newInference = "enable"
    }
}

Beholder写道:

  

原因是编译器设计中存在技术难题

我看到它的方式,Kotlin编译器不希望为具有2^n参数的方法生成n变体,这些参数有资格进行SAM转换,因此它只生成两个变体:一个人都是lambdas,一个人没有lambdas

YouTrack上存在相关问题:Impossible to pass not all SAM argument as function