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

are*_*lek 9 java android interop kotlin 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 {
    }
}
Run Code Online (Sandbox Code Playgroud)

而这个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
}
Run Code Online (Sandbox Code Playgroud)

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


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

are*_*lek 1

这将在 Kotlin 1.4 中修复,请参阅https://blog.jetbrains.com/kotlin/2020/03/kotlin-1-4-m1-released/

\n\n
\n

Kotlin 从一开始就支持 Java 接口的 SAM 转换,但有一种情况不支持 \xe2\x80\x99t,这在使用现有 Java 库时有时会很烦人。如果您调用采用两个 SAM 接口作为参数的 Java 方法,则两个参数都必须是 lambda 或常规对象。\xe2\x80\x99 不可能将一个参数作为 lambda 传递,将另一个参数作为对象传递。新算法解决了这个问题,在任何情况下您都可以传递 lambda 而不是 SAM 接口,这就是您\xe2\x80\x99d 自然期望它工作的方式。

\n
\n\n
\n\n

随着即将推出的新类型推断,这个问题将在 Kotlin 编译器中得到解决。现在可以通过将以下内容添加到模块级 gradle 文件中来在 Android 项目中启用实验类型推断(需要 Kotlin 1.3):

\n\n
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {\n    kotlinOptions {\n        freeCompilerArgs = ["-XXLanguage:+NewInference"]\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

应该可以通过以下方式启用它(但它还不太有效):

\n\n
kotlin {\n    experimental {\n        newInference = "enable"\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

旁观者写道:

\n\n
\n

造成这种情况的原因是编译器设计的技术难度

\n
\n\n

在我看来,Kotlin 编译器不想为2^n具有符合 SAM 转换条件的参数的方法生成变体n,因此它只生成两种变体:一种全部都是 lambda,一种没有 lambda拉姆达

\n\n

YouTrack 上有一个相关问题:Impossible to pass not all SAM argument as function

\n