Как преобразовать справочный код метода Java в Kotlin

У меня есть этот код на Java:

mViewModel.getSetupData().observe(this, this::updateTime);

Метод updateTime принимает один параметр. При преобразовании этого кода в Kotlin не работает следующее:

mViewModel?.getSetupData()?.observe(this, ::updateTime)

IDE (Android Studio) жалуется на

Несоответствие типов. Требуется: Наблюдатель. Найден KFunction1.

Любые подсказки?


person IgorGanapolsky    schedule 28.08.2019    source источник
comment
Каково точное сообщение об ошибке?   -  person Erwin Bolwidt    schedule 28.08.2019
comment
Синтаксис выглядит хорошо. Попробуйте построить с помощью gradle. Иногда перезапуск IDE помогает решить проблемы с выводом типов.   -  person Max Farsikov    schedule 28.08.2019
comment
Проблема связана с тем, что это должен быть Kotlin val androidx.lifecycle.Observer, а не функция.   -  person IgorGanapolsky    schedule 28.08.2019


Ответы (3)


По какой-то причине Kotlin недостаточно умен в обеспечении observe перегрузок:

// Java declaration
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer)

Теперь в Котлине мы можем вызывать этот метод двумя способами:

// Default Kotlin conversion [ #1 ]
fun observe(owner : LifecycleOwner, observer : Observer<in T>)

// SAM conversions [ #2 ]
fun observe(owner: () -> Lifecycle, observer : (T) -> Unit)

Чего нам не хватает, так это чего-то промежуточного, где мы предоставляем функцию только для аргумента наблюдателя.

Если у вас уже есть зависимость от модулей Android KTX, то расширение androidx.fragment:fragment-ktx (или хотя бы androidx.lifecycle:lifecycle-livedata-core-ktx) ниже уже должно быть доступно. В противном случае вы можете быстро добавить его:

// custom extension with function observer argument [ #3 ]
@MainThread inline fun <T> LiveData<T>.observe(
    owner: LifecycleOwner,
    crossinline onChanged: (T) -> Unit
): Observer<T> {
    val wrappedObserver = Observer<T> { t -> onChanged.invoke(t) }
    observe(owner, wrappedObserver)
    return wrappedObserver
}

Теперь вот разница в вызове:

// reusable observer object
val myObserver = Observer<TimeMeasurement> { updateTime(it) }

// function that updates time
fun updateTime(time : TimeMeasurement){
    // update views with time
}

override fun onCreate(savedInstanceState: Bundle?) {
    /* ... */

    // default call [ #1 ]
    mViewModel.liveData.observe(this, myObserver)
    // default call but with object expression for observer [ #1 ]
    mViewModel.liveData.observe(this, Observer { updateTime(it) })
    // alternatively [ #1 ]
    mViewModel.liveData.observe(this, Observer(::updateTime))

    // SAM conversions, lets us pass function reference or a lambda [ #2 ]
    // in this case first argument is a lambda as well and it returns lifecycle
    mViewModel.liveData.observe({ lifecycle }, ::updateTime)

    // extension function call where we pass function reference as second argument [ #3 ]
    mViewModel.liveData.observe(this, ::updateTime)
    // or using lambda [ #3 ]
    mViewModel.liveData.observe(this) { updateTime(it) }
}
person Pawel    schedule 28.08.2019
comment
Спасибо за разные примеры. Что такое crossinline в вашем коде? - person IgorGanapolsky; 29.08.2019
comment
Это фрагмент кода из Android KTX. crossinline предназначен для предотвращения нелокальных возвратов из орган наблюдателя. - person Pawel; 29.08.2019

Похоже, вы можете предоставить

.observe(this, Observer { someClass ->
    /*Here you can call your method*/
})

Да, это немного более многословно, чем метод Java, но по этой причине Kotlin не может вывести тип лямбда.

person asm0dey    schedule 28.08.2019
comment
Спасибо, и он по-прежнему жалуется на несоответствие типов. Вот подпись наблюдения: observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) developer.android.com/reference/android/arch/lifecycle/ - person IgorGanapolsky; 28.08.2019

Для тех, кто сталкивается с той же проблемой, это должен быть Kotlin val из API Android LiveData. Это не может быть функцией. Вот правильный код updateTime, который работает вместо функции:

    private val updateTime = Observer<TimeMeasurement> { 
      setupTimeMeasurement ->
      // Update the UI, in this case, a TextView.
      updateTimerData(setupTimeMeasurement, mViewModel!!.currentTimeValue)
}

И тогда вы можете назвать это так:

    mViewModel?.getCountdownData()?.observe(this, updateTime)
person IgorGanapolsky    schedule 28.08.2019