Kotlin: Interface ... non ha costruttori


138

Sto convertendo parte del mio codice Java in Kotlin e non capisco bene come creare un'istanza delle interfacce definite nel codice Kotlin. Ad esempio, ho un'interfaccia (definita nel codice Java):

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

E poi ulteriormente nel mio codice Kotlin ho un'istanza di questa interfaccia:

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

e funziona benissimo. Tuttavia, quando converto MyInterface in Kotlin:

interface MyInterface {
    fun onLocationMeasured(location: Location)
}

Viene visualizzato un messaggio di errore: Interface MyListener does not have constructorsquando provo ad istanziarlo, anche se mi sembra che nulla sia cambiato tranne la sintassi. Posso fraintendere come funzionano le interfacce in Kotlin?

Risposte:


225

Il codice Java si basa sulla conversione SAM, una conversione automatica di una lambda in un'interfaccia con un singolo metodo astratto. La conversione SAM non è attualmente supportata per le interfacce definite in Kotlin. Invece, è necessario definire un oggetto anonimo che implementa l'interfaccia:

val obj = object : MyInterface {
    override fun onLocationMeasured(location: Location) { ... }
}

14
Grazie molto. Dal link che hai pubblicato, capisco che è preferibile utilizzare tipi funzionali (ad esempio Location -> Unit) anziché interfacce a metodo singolo, se possibile - è corretto?
Aleph Aleph,

4
È corretto. Dovresti usare il tipo funzionale ove possibile.
Yoav Sternberg,

Tuttavia nel mio caso l'interfaccia (SurfaceTextureListener) aveva diversi metodi.
Tash Pemhiwa,

Grazie mille. Questa risposta deve avere più Mi piace o qualche segno speciale, può essere un'informazione molto utile e sfortunatamente alcuni studenti possono diventare molto confusi quando imparano Kotlin dagli articoli o da "Kotlin in azione" quando guardano l'argomento SAM.
TT_W,

da "Kotlin in azione", sì, puoi usare lambda nel parametro SAM di Java per abbreviare e pulire il codice, ma non SAM di Kotlin, il tipo di funzione è la prima classe in Kotlin, quindi SAM non ha significato per Kotlin, tipo di funzione con typealias è più stile Kotlin.
vg0x00,

17

La soluzione migliore è utilizzare una typealias al posto dell'interfaccia Java

typealias MyInterface = (Location) -> Unit

fun addLocationHandler(myInterface:MyInterface) {

}

Registralo in questo modo:

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

o anche più pulito

addLocationHandler { location -> ...}

Invocalo in questo modo:

myInterface.invoke(location)

Le 3 opzioni attuali sembrano essere:

  • typealias (disordinato quando chiamato da Java)
  • interfaccia kotlin (disordinata quando viene chiamata da kotlin; è necessario creare un oggetto) Questo è un grande passo indietro IMO.
  • interfaccia java (meno disordinata quando chiamata da kotlin; lambda ha bisogno di nome dell'interfaccia così non hai bisogno di un oggetto; inoltre non puoi usare lambda al di fuori della convenzione tra parentesi di funzione)

Quando abbiamo convertito le nostre librerie in Kotlin, in realtà abbiamo lasciato tutte le interfacce nel codice Java, poiché era più pulito chiamare Java da Kotlin che Kotlin da Kotlin.


8

Prova ad accedere alla tua interfaccia in questo modo:

 object : MyInterface {
    override fun onSomething() { ... }
}

6

se hai una classe Java come questa:

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

devi convertire questo codice da Java a Kotlin in questo modo:

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

convertire l' interfaccia Java :

new RecyclerTouchListener.ClickListener()

allo stile dell'interfaccia di Kotlin :

object : RecyclerTouchListener.ClickListener

1

Se l'interfaccia è per un metodo listener di una classe, cambia la definizione dell'interfaccia nel tipo di funzione. Ciò rende il codice più conciso. Vedi quanto segue.

Classe contenente la definizione di listener

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

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

// somewhere in A class
mLocationMeasuredListener(location)

Un'altra lezione

// B class
aClass.setOnLocationMeasuredListener { location ->
    // your code
}

-1
class YourClass : YourInterface {  
    override fun getTest() = "test"    
}

interface YourInterface {
    fun getTest(): String
}

val objectYourClass: YourInterface = YourClass()
print(objectYourClass.getTest())
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.