Kotlin SAM/Functional Interface throws AbstractMethodError



为什么SamTest2接口在访问other属性时会抛出AbstractMethodError?接口中提供了默认实现,因此我假设在main方法中创建的匿名实例可以正常工作。这是一个错误的假设吗?

fun interface SamTest1 {
fun method1(value: String): Boolean
fun other(): String = "test"
}
fun interface SamTest2 {
fun method1(value: String): Boolean
val other: String
get() = "test"
}
fun main() {
val sam1 = SamTest1 { true }
println(sam1.other()) // test
val sam2 = SamTest2 { true }
println(sam2.other) // AbstractMethodError
}

我正在查看为代码生成的JVM字节码。SamTest1SamTest2看起来几乎相同(除了一些内部事物的名称),这是有意义的,因为在JVM端,val与相同类型的0参数fun没有什么不同。

public interface SamTest1 {
public boolean method1(@NotNull String var1);
public String other();
public static final class DefaultImpls {
public static String other(SamTest1 this_) {
return "test";
}
}
}
public interface SamTest2 {
public boolean method1(@NotNull String var1);
public String getOther();
public static final class DefaultImpls {
public static String getOther(SamTest1 this_) {
return "test";
}
}
}

当我们看到你的main时,令人兴奋的部分就来了。清理一些工件,它看起来像

static final class sam1$1 implements SamTest1 {
public static final sam1$1 INSTANCE = new sam1$1();
public final boolean method1(String it) {
return true;
}
public String other() {
return SamTest1.DefaultImpls.other(this);
}
}
public static final void main() {
SamTest1 sam12 = sam1$1.INSTANCE;
System.out.println(sam12.other());
SamTest2 sam2 = samKt::main$lambda$0;
System.out.println(sam2.getOther());
}
private static final boolean main$lambda$0(String it) {
return true;
}
因此,据我所知,Kotlin认识到SamTest1有两个方法,因此(就Java而言)不是SAM接口,因此它创建了一个新类来表示您的匿名实例。方法的默认值在匿名类中传递(就像在Kotlin中应该发生的那样),一切都很好。

但是看起来Kotlin认为SamTest2与Java的SAM接口机制兼容,并简单地传递给它一个方法samKt::main$lambda$0,希望JVM端的SAM机制能够综合地构造对象。如果实际上有一个抽象方法,这个工作得很好,但是在JVM端有两个:method1getOther,因此后者在调用时得到AbstractMethodError

我很想证明自己是错的,但我认为这是Kotlin的bug。它应该为每个类生成合成类。我在错误跟踪器上找不到任何类似的问题,但我认为编译器的行为在这里是不正确的。


注意:我使用CFR将JVM字节码反编译回Java。我删除了很多空检查、元数据注释和类型强制转换,以使本文更简洁。

最新更新