Котлін: Інтерфейс ... не має конструкторів


138

Я перетворюю частину свого Java-коду в Котлін, і я не зовсім розумію, як інстанціювати інтерфейси, визначені в коді Котліна. Як приклад, у мене є інтерфейс (визначений в коді Java):

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

А далі в своєму коді Котліна я інстанціюю цей інтерфейс:

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

і це чудово працює. Однак, коли я перетворюю MyInterface в Котлін:

interface MyInterface {
    fun onLocationMeasured(location: Location)
}

Я отримую повідомлення про помилку: Interface MyListener does not have constructorsколи я намагаюся його інстанціювати - хоча мені здається, що нічого, крім синтаксису, не змінилося. Чи я неправильно розумію, як працюють інтерфейси в Котліні?

Відповіді:


226

Ваш код Java покладається на перетворення SAM - автоматичне перетворення лямбда в інтерфейс єдиним абстрактним методом. Наразі перетворення SAM не підтримується для інтерфейсів, визначених у Kotlin. Замість цього потрібно визначити анонімний об’єкт, що реалізує інтерфейс:

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

14
Велике дякую. З посилання, яке ви опублікували, я розумію, що краще використовувати функціональні типи (наприклад Location -> Unit) замість однометодних інтерфейсів, якщо можливо - це правильно?
Алеф Алеф

4
Це правильно. Ви повинні використовувати функціональний тип, де це можливо.
Йоав Штернберг

Однак у моєму випадку в інтерфейсі (SurfaceTextureListener) було кілька методів.
Таш Пемхіва

Дуже дякую. Ця відповідь повинна мати більше лайків або якусь спеціальну позначку, бо це дуже корисна інформація, і, на жаль, деякі учні можуть дуже заплутатися, коли дізнаються Котліна за статтями або "Котлін в дії", коли вони дивляться на тему SAM.
TT_W

від "Kotlin in Action", так, ви можете використовувати лямбда в параметрі SAM для скорочення та більш чистого коду, але не САМ Котліна, тип функції є першим класом у Kotlin, тому SAM не має значення для Kotlin, тип функції з typealias це більше стиль Котліна.
vg0x00

17

Найкраще рішення - використовувати типалії замість вашого інтерфейсу Java

typealias MyInterface = (Location) -> Unit

fun addLocationHandler(myInterface:MyInterface) {

}

Зареєструйте його так:

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

або навіть чистіше

addLocationHandler { location -> ...}

Викликайте так:

myInterface.invoke(location)

3 поточні варіанти, схоже, є:

  • typealias (безладний, коли дзвонять з Java)
  • інтерфейс kotlin (безладний, коли викликається з kotlin; вам потрібно створити об’єкт) Це великий крок назад ІМО.
  • java-інтерфейс (менш брудний, коли викликається з kotlin; лямбда потребує попереднього імені інтерфейсу, тому вам не потрібен об'єкт; також не можна використовувати лямбда поза функцією дужок)

Перетворюючи наші бібліотеки в Котлін, ми фактично залишили всі інтерфейси в коді Java, оскільки було чіткіше викликати Java з Котліна, ніж Котлін з Котліна.


8

Спробуйте отримати доступ до вашого інтерфейсу так:

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

6

якщо у вас клас Java такий:

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

Ви повинні перетворити цей код з Java в Котлін так:

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

конвертувати Java-інтерфейс :

new RecyclerTouchListener.ClickListener()

до стилю інтерфейсу Котліна:

object : RecyclerTouchListener.ClickListener

1

Якщо інтерфейс призначений для слухача методом класу, змініть визначення інтерфейсу на тип функції. Це робить код більш стислим. Дивіться наступне.

Клас, що містить визначення слухача

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

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

// somewhere in A class
mLocationMeasuredListener(location)

Ще один клас

// 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())
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.