Heute lief mir ein merkwürdiger Laufzeitfehler während der Entwicklung von Kotlin/Android, die SAM-Konvertierungen und Unterklassierung beinhaltet.Kotlin SAM Laufzeitfehler: NoSuchMethodError: Keine statische Methode
Hier ist ein minimales Beispiel für reines Java + Kotlin. Hier sind zwei Java-Klassen:
public class A {
public interface I {
public void f();
}
public I i;
}
public class B extends A {}
Und hier ist eine Kotlin Hauptfunktion:
fun main(args: Array<String>) {
A().i = B.I {}
}
Dieser Code kompiliert gut, aber während der Laufzeit erhalte ich folgende Fehlermeldung:
Exception in thread "main" java.lang.NoSuchMethodError: B.I(Lkotlin/jvm/functions/Function0;)LA$I;
at MainKt.main(Main.kt:2)
Jetzt , das ist schon schlecht - wenn Code wie dieser nicht funktioniert (ich denke nie), sollte der Compiler einen Fehler auslösen. Aber zumindest könnte man sagen, dass es eine schlechte Idee ist, sich auf die Schnittstelle I
über die Unterklasse B
anstelle des Ortes der Definition A
(d. H. A.I
) zu beziehen. obwohl
Es ist weniger klar, wenn dieser Code in einer Unterklasse von B
ist, wo ich direkt verweisen kann I
I
mit:
class C: B {
constructor() {
this.i = I {}
}
}
So würde meine Fragen sein:
- Warum ist dies Verhalten passiert überhaupt?
- Wenn es passiert, warum ist der Compiler nicht bereits einen Fehler?
PS: In android die Fehlermeldung sieht dies ähnlich, was noch verwirrender ist:
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)
es, dass Frontend scheint erkannt ' BI {} 'als Funktionsaufruf mit Lambda-Argument außerhalb von Klammern. Ich wette, dass dies ein Ergebnis fehlgeschlagener Annahmen ist. Verifier (oder wie auch immer sie es nennen) hat gefunden, dass "B.I" eine gültige SAM-Schnittstelle durch irgendeine Namensregistrierung bezeichnet, die der JLS folgt (erlaubt Supertype statische Mitglieder, auf die durch Subtypen verwiesen wird). Das Compiler-Backend, dessen Namensystem nicht JLS folgt (weil Kotlin nicht statisch ist), hat jedoch 'B.I' * in' B.java' nicht deklariert, also erwartet es, dass es sich um einen Funktionsaufruf handelt. Dies ist jedoch nur eine zufällige Schätzung. – glee8e
Schluss mit meiner Vermutung: Das Compiler Frontend und Backend haben unterschiedliche Richtlinien bezüglich * ob statische Supertype Member die über Subtypen referenziert sind *. Sie sollten ein Problem auf [Kotlin Youtrack] (https://youtrack.jetbrains.com/issues/KT) einreichen. – glee8e
https://youtrack.jetbrains.com/issue/KT-18745 – dpoetzsch