今天我遇到了一個非常奇怪的運行時錯誤,同時開發涉及SAM轉換和子分類的kotlin/android。Kotlin SAM運行時錯誤:NoSuchMethodError:沒有靜態方法
下面是純java + kotlin的一個簡單示例。這裏有兩個Java類:
public class A {
public interface I {
public void f();
}
public I i;
}
public class B extends A {}
,這裏是一個科特林主要功能:
fun main(args: Array<String>) {
A().i = B.I {}
}
此代碼編譯正常,但在運行時我收到以下錯誤:
Exception in thread "main" java.lang.NoSuchMethodError: B.I(Lkotlin/jvm/functions/Function0;)LA$I;
at MainKt.main(Main.kt:2)
現在,這已經很糟糕了 - 如果這樣的代碼不起作用(我從來不會猜到)編譯器應該引發錯誤。但至少有人可以說,通過子類B
而不是定義A
(即,A.I
)的地址參考接口I
是不好的主意。
這是不太清楚不過,如果這個代碼是在子類的B
在那裏我可以參考I
直接使用I
:
class C: B {
constructor() {
this.i = I {}
}
}
所以我的問題是:
- 這是爲什麼行爲發生在所有?
- 如果正在發生,爲什麼編譯器不會引發錯誤?
PS:在Android上的錯誤信息類似於此,它是更加混亂:
Caused by: java.lang.NoSuchMethodError: No static method OnFocusChangeListener(Lkotlin/jvm/functions/Function2;)Landroid/view/View$OnFocusChangeListener; in class Landroid/widget/LinearLayout; or its super classes (declaration of 'android.widget.LinearLayout' appears in /system/framework/framework.jar:classes2.dex)
主要方法似乎前端認可' BI {}'作爲函數調用,lambda參數在括號外。我敢打賭,這是失敗假設的結果。 Verifier(或者他們命名的)確實發現'B.I'表示通過JLS後面的某個名稱註冊中心(允許通過子類型引用超類型靜態成員)的有效SAM界面。編譯器後端的名稱系統不遵循JLS(因爲kotlin沒有靜態),但是在'B.java'中沒有發現'B.I' *聲明*,所以它期望它是一個函數調用。這只是一個隨機猜測。 – glee8e
結論我的猜測:編譯器前端和後端有不同的政策*是否允許通過子類型引用的超類型靜態成員*。您應該在[kotlin youtrack](https://youtrack.jetbrains.com/issues/KT)上提出問題。 – glee8e
https://youtrack.jetbrains.com/issue/KT-18745 – dpoetzsch