Kotlin: Interface… n'a pas de constructeurs

138

Je convertis une partie de mon code Java en Kotlin et je ne comprends pas très bien comment instancier les interfaces définies dans le code Kotlin. A titre d'exemple, j'ai une interface (définie en code Java):

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

Et puis plus loin dans mon code Kotlin, j'instancie cette interface:

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

et cela fonctionne très bien. Cependant, lorsque je convertis MyInterface en Kotlin:

interface MyInterface {
    fun onLocationMeasured(location: Location)
}

J'obtiens un message d'erreur: Interface MyListener does not have constructorslorsque j'essaye de l'instancier - bien qu'il me semble que rien n'a changé sauf la syntaxe. Est-ce que je comprends mal comment les interfaces fonctionnent dans Kotlin?

Aleph Aleph
la source

Réponses:

225

Votre code Java repose sur la conversion SAM - une conversion automatique d'un lambda en une interface avec une seule méthode abstraite. La conversion SAM n'est actuellement pas prise en charge pour les interfaces définies dans Kotlin. Au lieu de cela, vous devez définir un objet anonyme implémentant l'interface:

val obj = object : MyInterface {
    override fun onLocationMeasured(location: Location) { ... }
}
yole
la source
14
Merci beaucoup. D'après le lien que vous avez publié, je comprends qu'il est préférable d'utiliser des types fonctionnels (par exemple Location -> Unit) au lieu d'interfaces à méthode unique si possible - est-ce exact?
Aleph Aleph
4
C'est correct. Vous devez utiliser le type fonctionnel dans la mesure du possible.
Yoav Sternberg
Cependant, dans mon cas, l'interface (SurfaceTextureListener) avait plusieurs méthodes.
Tash Pemhiwa
Merci beaucoup. Cette réponse doit avoir plus de likes ou une note spéciale, car c'est une information très utile et malheureusement certains apprenants peuvent devenir très confus lorsqu'ils apprennent Kotlin par des articles ou par "Kotlin in Action" lorsqu'ils regardent le sujet SAM.
TT_W
de "Kotlin in Action", oui, vous pouvez utiliser lambda dans le paramètre SAM de Java pour raccourcir et nettoyer le code, mais pas le SAM de Kotlin, le type de fonction est la première classe de Kotlin, donc, SAM n'a pas de signification pour Kotlin, type de fonction avec typealias est plus de style Kotlin.
vg0x00
17

La meilleure solution est d'utiliser des typealias à la place de votre interface Java

typealias MyInterface = (Location) -> Unit

fun addLocationHandler(myInterface:MyInterface) {

}

Enregistrez-le comme ceci:

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

ou même plus propre

addLocationHandler { location -> ...}

Invoquez-le comme ceci:

myInterface.invoke(location)

Les 3 options actuelles semblent être:

  • typealias (désordonné lorsqu'il est appelé depuis java)
  • interface kotlin (désordonné lorsqu'il est appelé de kotlin; vous devez créer un objet) C'est un grand pas en arrière IMO.
  • interface java (moins salissante lorsqu'elle est appelée depuis kotlin; lambda a besoin d'un nom d'interface en préfixe donc vous n'avez pas besoin d'un objet; ne peut pas non plus utiliser lambda en dehors de la convention de parenthèse de fonction)

Lors de la conversion de nos bibliothèques vers Kotlin, nous avons en fait laissé toutes les interfaces en code Java, car il était plus facile d'appeler Java depuis Kotlin que Kotlin depuis Kotlin.

Steven Spungin
la source
8

Essayez d'accéder à votre interface comme ceci:

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

si vous avez une classe Java comme celle-ci:

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

vous devez convertir ce code de Java en Kotlin comme ceci:

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

convertir l'interface Java :

new RecyclerTouchListener.ClickListener()

au style d' interface Kotlin :

object : RecyclerTouchListener.ClickListener
Adnan Abdollah Zaki
la source
1

Si l'interface est destinée à une méthode d'écoute d'une classe, remplacez la définition d'interface par le type de fonction. Cela rend le code plus concis. Voir ce qui suit.

Classe contenant la définition d'écouteur

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

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

// somewhere in A class
mLocationMeasuredListener(location)

Une autre classe

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

interface YourInterface {
    fun getTest(): String
}

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