Kotlin: Interfejs… nie ma konstruktorów

138

Konwertuję część mojego kodu Java do Kotlin i nie do końca rozumiem, jak tworzyć instancje interfejsów, które są zdefiniowane w kodzie Kotlin. Jako przykład mam interfejs (zdefiniowany w kodzie Java):

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

A potem dalej w moim kodzie Kotlin tworzę instancję tego interfejsu:

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

i działa dobrze. Jednak kiedy konwertuję MyInterface na Kotlin:

interface MyInterface {
    fun onLocationMeasured(location: Location)
}

Otrzymuję komunikat o błędzie: Interface MyListener does not have constructorskiedy próbuję go utworzyć - chociaż wydaje mi się, że nic się nie zmieniło poza składnią. Czy źle rozumiem, jak działają interfejsy w Kotlinie?

Aleph Aleph
źródło

Odpowiedzi:

225

Twój kod Java opiera się na konwersji SAM - automatycznej konwersji lambdy na interfejs z pojedynczą metodą abstrakcyjną. Konwersja SAM nie jest obecnie obsługiwana dla interfejsów zdefiniowanych w Kotlin. Zamiast tego musisz zdefiniować anonimowy obiekt implementujący interfejs:

val obj = object : MyInterface {
    override fun onLocationMeasured(location: Location) { ... }
}
yole
źródło
14
Wielkie dzięki. Z linku, który zamieściłeś, rozumiem, że lepiej jest używać typów funkcjonalnych (np. Location -> Unit) Zamiast interfejsów jednomodowych, jeśli to możliwe - czy to prawda?
Aleph Aleph
4
Jest prawidłowa. Tam, gdzie to możliwe, należy używać typu funkcjonalnego.
Yoav Sternberg
Jednak w moim przypadku interfejs (SurfaceTextureListener) miał wiele metod.
Tash Pemhiwa
Dzięki wielkie. Ta odpowiedź musi mieć więcej polubień lub jakąś specjalną ocenę, ponieważ jest to bardzo przydatna informacja i niestety niektórzy uczniowie mogą być bardzo zdezorientowani, gdy uczą się Kotlina z artykułów lub z "Kotlin w akcji", gdy patrzą na temat SAM.
TT_W
z "Kotlin w akcji", tak, możesz użyć lambdy w parametrze SAM Javy, aby skrócić i uporządkować kod, ale nie SAM Kotlina, typ funkcji jest pierwszą klasą w Kotlinie, więc SAM nie ma znaczenia dla Kotlina, typ funkcji z aliasami typów jest bardziej w stylu Kotlin.
vg0x00
17

Najlepszym rozwiązaniem jest użycie aliasu typu zamiast interfejsu Java

typealias MyInterface = (Location) -> Unit

fun addLocationHandler(myInterface:MyInterface) {

}

Zarejestruj to w ten sposób:

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

lub nawet czystszy

addLocationHandler { location -> ...}

Wywołaj to w ten sposób:

myInterface.invoke(location)

Wydaje się, że 3 obecne opcje to:

  • typealias (niechlujny przy wywoływaniu z javy)
  • Interfejs kotlin (niechlujny, gdy jest wywoływany z kotlin; musisz utworzyć obiekt) To jest duży krok wstecz IMO.
  • interfejs java (mniej bałaganu wywoływany z kotlin; lambda wymaga dołączonej nazwy interfejsu, więc nie potrzebujesz obiektu; nie można również używać lambdy poza konwencją nawiasów funkcji)

Konwertując nasze biblioteki na Kotlin, właściwie zostawiliśmy wszystkie interfejsy w kodzie Javy, ponieważ czystsze było wywołanie Javy z Kotlina niż Kotlina z Kotlina.

Steven Spungin
źródło
8

Spróbuj uzyskać dostęp do swojego interfejsu w następujący sposób:

 object : MyInterface {
    override fun onSomething() { ... }
}
Jéwôm ”
źródło
6

jeśli masz taką klasę Java :

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

powinieneś przekonwertować ten kod z Java do Kotlin w następujący sposób:

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

konwertuj interfejs Java :

new RecyclerTouchListener.ClickListener()

do stylu interfejsu Kotlin :

object : RecyclerTouchListener.ClickListener
Adnan Abdollah Zaki
źródło
1

Jeśli interfejs jest przeznaczony dla metody nasłuchującej klasy, zmień definicję interfejsu na typ funkcji. To sprawia, że ​​kod jest bardziej zwięzły. Zobacz poniżej.

Klasa zawierająca definicję detektora

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

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

// somewhere in A class
mLocationMeasuredListener(location)

Inna klasa

// B class
aClass.setOnLocationMeasuredListener { location ->
    // your code
}
Shin Seungwoo
źródło
-1
class YourClass : YourInterface {  
    override fun getTest() = "test"    
}

interface YourInterface {
    fun getTest(): String
}

val objectYourClass: YourInterface = YourClass()
print(objectYourClass.getTest())
Braian Coronel
źródło