Kotlin: Schnittstelle ... hat keine Konstruktoren

138

Ich konvertiere einen Teil meines Java-Codes in Kotlin und verstehe nicht ganz, wie man im Kotlin-Code definierte Schnittstellen instanziiert. Als Beispiel habe ich eine Schnittstelle (definiert in Java-Code):

public interface MyInterface {
    void onLocationMeasured(Location location);
}

Und weiter in meinem Kotlin-Code instanziiere ich diese Schnittstelle:

val myObj = new MyInterface { Log.d("...", "...") }

und es funktioniert gut. Wenn ich jedoch MyInterface in Kotlin konvertiere:

interface MyInterface {
    fun onLocationMeasured(location: Location)
}

Ich erhalte eine Fehlermeldung: Interface MyListener does not have constructorsWenn ich versuche, sie zu instanziieren - obwohl mir scheint, dass sich außer der Syntax nichts geändert hat. Verstehe ich falsch, wie Schnittstellen in Kotlin funktionieren?

Aleph Aleph
quelle

Antworten:

225

Ihr Java-Code basiert auf der SAM-Konvertierung - einer automatischen Konvertierung eines Lambda in eine Schnittstelle mit einer einzigen abstrakten Methode. Die SAM-Konvertierung wird derzeit für in Kotlin definierte Schnittstellen nicht unterstützt . Stattdessen müssen Sie ein anonymes Objekt definieren, das die Schnittstelle implementiert:

val obj = object : MyInterface {
    override fun onLocationMeasured(location: Location) { ... }
}
yole
quelle
14
Danke vielmals. Aus dem von Ihnen geposteten Link geht hervor, dass es vorzuziehen ist, Funktionstypen (z. B. Location -> Unit) anstelle von Schnittstellen mit nur einer Methode zu verwenden, wenn dies möglich ist - ist das richtig?
Aleph Aleph
4
Es ist richtig. Sie sollten nach Möglichkeit den Funktionstyp verwenden.
Yoav Sternberg
In meinem Fall hatte die Schnittstelle (SurfaceTextureListener) jedoch mehrere Methoden.
Tash Pemhiwa
Vielen Dank. Diese Antwort muss mehr Likes oder eine spezielle Markierung haben, da es sich um sehr nützliche Informationen handelt. Leider können einige Lernende sehr verwirrt sein, wenn sie Kotlin anhand von Artikeln oder "Kotlin in Aktion" lernen, wenn sie sich das SAM-Thema ansehen.
TT_W
von "Kotlin in Aktion", ja, Sie können Lambda in Javas SAM-Parameter für kürzeren und saubereren Code verwenden, aber nicht in Kotlins SAM. Der Funktionstyp ist die erste Klasse in Kotlin. Daher hat SAM keine Bedeutung für Kotlin, Funktionstyp mit Typealien ist mehr Kotlin-Stil.
vg0x00
17

Die beste Lösung besteht darin, anstelle Ihrer Java-Schnittstelle Typealias zu verwenden

typealias MyInterface = (Location) -> Unit

fun addLocationHandler(myInterface:MyInterface) {

}

Registrieren Sie es so:

val myObject = { location -> ...}
addLocationHandler(myObject)

oder noch sauberer

addLocationHandler { location -> ...}

Rufen Sie es so auf:

myInterface.invoke(location)

Die 3 aktuellen Optionen scheinen zu sein:

  • typealias (chaotisch, wenn von Java aufgerufen)
  • Kotlin-Schnittstelle (chaotisch, wenn von Kotlin aufgerufen; Sie müssen ein Objekt erstellen) Dies ist ein großer Schritt zurück IMO.
  • Java-Schnittstelle (weniger chaotisch, wenn sie von Kotlin aufgerufen wird; Lambda muss einen vorangestellten Schnittstellennamen haben, damit Sie kein Objekt benötigen; Lambda kann auch nicht außerhalb der Konvention für Funktionsklammern verwendet werden).

Bei der Konvertierung unserer Bibliotheken in Kotlin haben wir tatsächlich alle Schnittstellen in Java-Code belassen, da es sauberer war, Java von Kotlin aus aufzurufen als Kotlin von Kotlin.

Steven Spungin
quelle
8

Versuchen Sie, wie folgt auf Ihre Benutzeroberfläche zuzugreifen:

 object : MyInterface {
    override fun onSomething() { ... }
}
Jéwôm '
quelle
6

Wenn Sie eine Java-Klasse wie diese haben:

recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getActivity(), recyclerView, new RecyclerTouchListener.ClickListener()
        {
              //Your Code
        }));

Sie sollten diesen Code wie folgt von Java nach Kotlin konvertieren :

override fun showJozList (list : List<ResponseGetJuzList.Parameter4>) {
        adapter.addData(list)
        jozlist_recycler.addOnItemTouchListener(RecyclerTouchListener(
                activity ,
                jozlist_recycler ,
                object : RecyclerTouchListener.ClickListener
                    {
                          //Your Code
                    }))

Java-Schnittstelle konvertieren :

new RecyclerTouchListener.ClickListener()

zu Kotlin Interface Style:

object : RecyclerTouchListener.ClickListener
Adnan Abdollah Zaki
quelle
1

Wenn die Schnittstelle für eine Listener-Methode einer Klasse bestimmt ist, ändern Sie die Schnittstellendefinition in den Funktionstyp. Das macht den Code prägnanter. Siehe folgendes.

Klasse mit Listener-Definition

// A class
private var mLocationMeasuredListener = (location: Location) -> Unit = {}

var setOnLocationMeasuredListener(listener: (location: Location) -> Unit) {
    mLocationMeasuredListener = listener
}

// somewhere in A class
mLocationMeasuredListener(location)

Eine andere Klasse

// B class
aClass.setOnLocationMeasuredListener { location ->
    // your code
}
Shin Seungwoo
quelle
-1
class YourClass : YourInterface {  
    override fun getTest() = "test"    
}

interface YourInterface {
    fun getTest(): String
}

val objectYourClass: YourInterface = YourClass()
print(objectYourClass.getTest())
Braian Coronel
quelle