Kotlin: Interfaz ... no tiene constructores

138

Estoy convirtiendo parte de mi código Java a Kotlin y no entiendo cómo instanciar interfaces que están definidas en el código de Kotlin. Como ejemplo, tengo una interfaz (definida en el código Java):

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

Y luego, en mi código de Kotlin, ejemplifico esta interfaz:

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

y funciona bien Sin embargo, cuando convierto MyInterface a Kotlin:

interface MyInterface {
    fun onLocationMeasured(location: Location)
}

Recibo un mensaje de error: Interface MyListener does not have constructorscuando intento crear una instancia, aunque me parece que nada ha cambiado excepto la sintaxis. ¿No entiendo cómo funcionan las interfaces en Kotlin?

Aleph Aleph
fuente

Respuestas:

225

Su código Java se basa en la conversión SAM: una conversión automática de un lambda en una interfaz con un único método abstracto. La conversión de SAM actualmente no es compatible con las interfaces definidas en Kotlin. En su lugar, debe definir un objeto anónimo que implemente la interfaz:

val obj = object : MyInterface {
    override fun onLocationMeasured(location: Location) { ... }
}
yole
fuente
14
Muchas gracias. Desde el enlace que publicó, entiendo que es preferible usar tipos funcionales (por ejemplo Location -> Unit) en lugar de interfaces de un solo método, si es posible, ¿es correcto?
Aleph Aleph
44
Es correcto. Debe usar el tipo funcional siempre que sea posible.
Yoav Sternberg
Sin embargo, en mi caso, la interfaz (SurfaceTextureListener) tenía varios métodos.
Tash Pemhiwa
Muchas gracias. Esta respuesta debe tener más Me gusta o alguna marca especial, porque es información muy útil y desafortunadamente algunos estudiantes pueden confundirse mucho cuando aprenden Kotlin por artículos o por "Kotlin en acción" cuando miran el tema SAM.
TT_W
de "Kotlin en acción", sí, puede usar lambda en el parámetro SAM de Java para acortar y limpiar el código, pero no el tipo de función SAM de Kotlin es la primera clase en Kotlin, por lo tanto, SAM no tiene significado para Kotlin, tipo de función con typealias Es más estilo Kotlin.
vg0x00
17

La mejor solución es usar un typealias en lugar de su interfaz Java

typealias MyInterface = (Location) -> Unit

fun addLocationHandler(myInterface:MyInterface) {

}

Regístralo así:

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

o incluso más limpio

addLocationHandler { location -> ...}

Invocarlo así:

myInterface.invoke(location)

Las 3 opciones actuales parecen ser:

  • typealias (desordenado cuando se llama desde java)
  • Interfaz kotlin (desordenada cuando se llama desde kotlin; necesita crear un objeto) Este es un gran paso atrás en la OMI.
  • interfaz java (menos complicado cuando se llama desde kotlin; lambda necesita el nombre de la interfaz antepuesto para que no necesite un objeto; tampoco puede usar lambda fuera de la convención de paréntesis de función)

Al convertir nuestras bibliotecas a Kotlin, en realidad dejamos todas las interfaces en código Java, ya que era más limpio llamar a Java desde Kotlin que a Kotlin desde Kotlin.

Steven Spungin
fuente
8

Intenta acceder a tu interfaz así:

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

si tienes una clase de Java como esta:

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

deberías convertir este código de Java a Kotlin así:

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

convertir la interfaz de Java :

new RecyclerTouchListener.ClickListener()

al estilo de interfaz Kotlin :

object : RecyclerTouchListener.ClickListener
Adnan Abdollah Zaki
fuente
1

Si la interfaz es para un método de escucha de una clase, cambie la definición de la interfaz al tipo de función. Eso hace que el código sea más conciso. Ver lo siguiente.

Clase que contiene la definición del oyente

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

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

// somewhere in A class
mLocationMeasuredListener(location)

Otra clase

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

interface YourInterface {
    fun getTest(): String
}

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