Kotlin: Interface… não possui construtores

138

Estou convertendo parte do meu código Java para Kotlin e não entendo como instanciar interfaces definidas no código Kotlin. Como exemplo, eu tenho uma interface (definida no código Java):

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

E depois, no meu código Kotlin, instanciamos essa interface:

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

e funciona bem. No entanto, quando eu converter MyInterface em Kotlin:

interface MyInterface {
    fun onLocationMeasured(location: Location)
}

Recebo uma mensagem de erro: Interface MyListener does not have constructorsquando tento instancia-lo - embora me pareça que nada mudou, exceto a sintaxe. Entendo mal como as interfaces funcionam no Kotlin?

Aleph Aleph
fonte

Respostas:

225

Seu código Java depende da conversão do SAM - uma conversão automática de um lambda em uma interface com um único método abstrato. Atualmente, a conversão de SAM não é suportada para interfaces definidas no Kotlin. Em vez disso, você precisa definir um objeto anônimo implementando a interface:

val obj = object : MyInterface {
    override fun onLocationMeasured(location: Location) { ... }
}
yole
fonte
14
Muito Obrigado. Pelo link que você postou, entendo que é preferível usar tipos funcionais (por exemplo Location -> Unit) em vez de interfaces de método único, se possível - está correto?
Aleph Aleph #
4
Isso está correto. Você deve usar o tipo funcional sempre que possível.
Yoav Sternberg
No entanto, no meu caso, a interface (SurfaceTextureListener) tinha vários métodos.
Tash Pemhiwa
Muito obrigado. Esta resposta deve ter mais gostos ou alguma marca especial, pois é uma informação muito útil e, infelizmente, alguns alunos podem ficar muito confusos ao aprender o Kotlin por artigos ou por "Kotlin em Ação" quando examinam o tópico do SAM.
TT_W 5/02/19
do "Kotlin in Action", sim, você pode usar o lambda no parâmetro SAM do Java para código mais curto e mais limpo, mas não o SAM do Kotlin, o tipo de função é a primeira classe no Kotlin; portanto, o SAM não tem significado para o Kotlin, tipo de função com tipealias é mais estilo Kotlin.
vg0x00 13/01
17

A melhor solução é usar uma tipealias no lugar da sua interface Java

typealias MyInterface = (Location) -> Unit

fun addLocationHandler(myInterface:MyInterface) {

}

Registre-o assim:

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

ou até mais limpo

addLocationHandler { location -> ...}

Invoque-o assim:

myInterface.invoke(location)

As três opções atuais parecem ser:

  • typealias (bagunçado quando chamado de java)
  • interface do kotlin (confuso quando chamado do kotlin; você precisa criar um objeto) Este é um grande passo para trás no IMO.
  • interface java (menos bagunçada quando chamada do kotlin; o lambda precisa de um nome de interface anexado para que você não precise de um objeto; também não pode usar o lambda fora da convenção de parênteses da função)

Ao converter nossas bibliotecas para o Kotlin, deixamos todas as interfaces no código Java, pois era mais fácil chamar Java do Kotlin do que Kotlin do Kotlin.

Steven Spungin
fonte
8

Tente acessar sua interface assim:

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

se você tiver classe Java como esta:

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

você deve converter esse código do Java para o Kotlin desta forma:

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

converter interface Java :

new RecyclerTouchListener.ClickListener()

ao estilo de interface Kotlin :

object : RecyclerTouchListener.ClickListener
Adnan Abdollah Zaki
fonte
1

Se a interface for para um método de ouvinte de uma classe, altere a definição da interface para o tipo de função. Isso torna o código mais conciso. Veja o seguinte.

Classe que contém a definição de ouvinte

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

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

// somewhere in A class
mLocationMeasuredListener(location)

Outra classe

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

interface YourInterface {
    fun getTest(): String
}

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