Котлин: Интерфейс ... не имеет конструкторов


138

Я конвертирую часть своего Java-кода в Kotlin, и я не совсем понимаю, как создавать экземпляры интерфейсов, которые определены в Kotlin-коде. В качестве примера у меня есть интерфейс (определенный в коде Java):

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

А затем в моем коде Kotlin я создаю этот интерфейс:

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

и работает нормально. Однако, когда я конвертирую MyInterface в Kotlin:

interface MyInterface {
    fun onLocationMeasured(location: Location)
}

Я получаю сообщение об ошибке: Interface MyListener does not have constructorsкогда я пытаюсь создать его экземпляр - хотя мне кажется, что ничего не изменилось, кроме синтаксиса. Я неправильно понимаю, как работают интерфейсы в Kotlin?

Ответы:


225

Ваш Java-код основан на преобразовании SAM - автоматическом преобразовании лямбды в интерфейс с помощью одного абстрактного метода. Преобразование SAM в настоящее время не поддерживается для интерфейсов, определенных в Kotlin. Вместо этого вам нужно определить анонимный объект, реализующий интерфейс:

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

14
Большое спасибо. Из ссылки, которую вы разместили, я понимаю, что предпочтительнее использовать функциональные типы (например Location -> Unit), а не интерфейсы с одним методом, если это возможно - это правильно?
Алеф Алеф

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

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

Спасибо. В этом ответе должно быть больше отметок «Нравится» или какая-то особая отметка, потому что это очень полезная информация, и, к сожалению, некоторые учащиеся могут очень запутаться, когда изучают Kotlin по статьям или «Kotlin in Action», когда смотрят на тему SAM.
TT_W

из "Kotlin в действии", да, вы можете использовать лямбду в параметре SAM Java в качестве сокращенного и более чистого кода, но не SAM Kotlin, тип функции является первым классом в Kotlin, поэтому SAM не имеет значения для Kotlin, тип функции с typealias это больше стиль Котлин.
vg0x00

17

Лучшее решение - использовать typealias вместо вашего Java-интерфейса.

typealias MyInterface = (Location) -> Unit

fun addLocationHandler(myInterface:MyInterface) {

}

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

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

или даже чище

addLocationHandler { location -> ...}

Вызовите это так:

myInterface.invoke(location)

3 текущих варианта:

  • typealias (грязный, когда вызывается из Java)
  • Интерфейс kotlin (грязный, когда вызывается из kotlin; вам нужно создать объект) Это большой шаг назад IMO.
  • Java-интерфейс (менее грязный при вызове из kotlin; лямбда нуждается в добавлении имени интерфейса, поэтому вам не нужен объект; также нельзя использовать лямбда вне соглашения о скобках функции)

При преобразовании наших библиотек в Kotlin мы фактически оставили все интерфейсы в коде Java, так как было проще вызывать Java из Kotlin, чем Kotlin из Kotlin.


8

Попробуйте получить доступ к вашему интерфейсу следующим образом:

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

6

если у вас есть класс Java, как это:

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

Вы должны преобразовать этот код из Java в Kotlin следующим образом:

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.