Перепишите Java-код в Kotlin, используя Справочник по функциям, возникает конфликт типов SAM

У меня есть пример кода Java, использующий ссылку на метод, который я хочу переписать на Kotlin. Версия Java использует ссылку на метод, решение краткое и ясное. Но с другой стороны, я не могу использовать ссылку на метод в Котлине. Единственная версия, которую мне удалось написать, представлена ​​ниже. Кажется, что Function3 { s: String, b: Boolean, i: Int -> combine(s, b, i) } можно было бы написать более чистым способом (если возможно, ссылка на метод была бы идеальной).

Я новичок в Котлине, поэтому буду благодарен за любые подсказки.

Java

import io.reactivex.Observable;

public class TestJava {

    Observable<String> strings() {
        return Observable.just("test");
    }

    Observable<Boolean> booleans() {
        return Observable.just(true);
    }

    Observable<Integer> integers() {
        return Observable.just(1);
    }

    void test() {
        Observable.combineLatest(strings(), booleans(), integers(),
                this::combine);
    }

    double combine(String s, boolean b, int i) {
        return 1.0;
    }
}

Котлин

import io.reactivex.Observable
import io.reactivex.functions.Function3

class TestKotlin {

    fun strings(): Observable<String> {
        return Observable.just("test")
    }

    fun booleans(): Observable<Boolean> {
        return Observable.just(true)
    }

    fun integers(): Observable<Int> {
        return Observable.just(1)
    }

    fun test() {
        Observable.combineLatest(strings(), booleans(), integers(),
                Function3 { s: String, b: Boolean, i: Int -> combine(s, b, i) })
    }

    fun combine(s: String, b: Boolean, i: Int): Double {
        return 1.0
    }
}

ИЗМЕНИТЬ

Следующие ссылки на методы в Kotlin выдают ошибку.

fun test() {
    Observable.combineLatest(strings(), booleans(), integers(), this::combine)
}

fun test() {
    Observable.combineLatest(strings(), booleans(), integers(), TestKotlin::combine)
}

Ни одна из следующих функций не может быть вызвана с указанными аргументами: @CheckReturnValue @SchedulerSupport public final fun combLatest (p0: ((Observer) -> Unit) !, p1: ((Observer) -> Unit) !, p2: (( Наблюдатель) -> Единица) !, p3: ((???, ???, ???) -> ???)!): Наблюдаемый ‹(??? .. ???)>! определено в io.reactivex.Observable @CheckReturnValue @SchedulerSupport public open fun combLatest (p0: ObservableSource !, p1: ObservableSource !, p2: ObservableSource !, p3: io.reactivex.functions.Function3!): Observable ‹(???. . ???)>! определено в io.reactivex.Observable @CheckReturnValue @SchedulerSupport public open fun combLatest (p0: Function !, out (??? .. ???)> !, p1: Int, vararg p2: ObservableSource!): Observable ‹(? ?? .. ???)>! определено в io.reactivex.Observable

ИЗМЕНИТЬ 2

RxKotlin решает проблему, упомянутую в ответе.

import io.reactivex.rxkotlin.Observables

class TestKotlin {

    fun test() {
        Observables.combineLatest(strings(), booleans(), integers(), this::combine)
    }

}

person Aleksander Mielczarek    schedule 15.07.2017    source источник
comment
Во-первых, я не депрессивный. ты пробовал?   -  person holi-java    schedule 15.07.2017
comment
Я не могу использовать ссылку на метод в Kotlin Почему нет?   -  person Jorn Vernee    schedule 15.07.2017
comment
@ holi-java конечно пробовал   -  person Aleksander Mielczarek    schedule 15.07.2017
comment
@AleksanderMielczarek. Вы пробовали: combineLatest(strings(), booleans(), integers(), this::combine) в Котлине?   -  person holi-java    schedule 15.07.2017
comment
@ holi-java см. отредактированный вопрос   -  person Aleksander Mielczarek    schedule 15.07.2017
comment
@AleksanderMielczarek Я ясно это вижу. вы пытаетесь ссылаться на функцию-член как на статическую функцию. вам нужно увидеть kotlinlang.org/docs/reference/   -  person holi-java    schedule 15.07.2017
comment
@ holi-java, использующий this :: comb, имеет ту же ошибку   -  person Aleksander Mielczarek    schedule 15.07.2017
comment
@AleksanderMielczarek, какую ошибку вы получили? вывод типа?   -  person holi-java    schedule 15.07.2017
comment
Ошибка @ holi-java добавлена ​​в вопрос   -  person Aleksander Mielczarek    schedule 15.07.2017
comment
@AleksanderMielczarek это потому, что rx-java2 имеет более одного combineLatest метода, он не знает, куда идти. это не проблема ссылочной функции.   -  person holi-java    schedule 15.07.2017
comment
@ holi-java, не могли бы вы объяснить мне, почему Java может использовать ссылку на метод, а Kotlin - нет?   -  person Aleksander Mielczarek    schedule 15.07.2017


Ответы (2)


Вы также можете использовать выражение ссылки на функцию в Kotlin точно так же, как в качестве справочника по методам Выражение в Java. например, ссылка на функцию combine как KFunction3 в Kotlin, как показано ниже:

val f: kotlin.reflect.KFunction3<String, Boolean, Int, Double> = this::combine

В java выражение ссылки на метод может быть присвоено любому совместимому Функциональный интерфейс, но вы не можете назначить Выражение ссылки на функцию для любых типов функций, даже если они совместимы, например:

val f:io.reactivex.functions.Function3<String,Boolean,Int,Double> =this::combine
//                                                type mismatch error     ---^

Действительно, Kotlin использует преобразования SAM для преобразования лямбда / Выражение ссылки на функцию в реализацию Java Функциональный интерфейс, что означает, что Kotlin выполняет следующие действия:

fun test() = TODO()

val exector:Executor = TODO()

exector.execute(::test)

//::test compile to java code as below:
Runnable task = new Runnable(){
   public void run(){
      test();
   }
};

Почему выражение "Method Reference Expression" может нормально работать в Java, но при использовании Выражение ссылки на функцию не работает в Котлине?

Основываясь на вышеизложенном, вы можете увидеть, что есть много перегруженных combineLatest методов в rx-java2. например, следующие два не могут заставить Kotlin использовать выражение ссылки на функцию правильно:

combineLatest(
       ObservableSource<out T1>,
       ObservableSource<out T2>,
       ObservableSource<out T3>, 
       Function3<T1, T2, T3, out R>
)

combineLatest(
       ObservableSource<out T1>,
       ObservableSource<out T2>,
       ObservableSource<out T3>, 
       ObservableSource<out T4>, 
       Function4<T1, T2, T3, T4, out R>
)

Как я уже сказал, Котлин использует преобразования SAM для преобразования лямбда / Справочное выражение функции для Java функциональный интерфейс, но его нельзя назначить Java Функциональный интерфейс напрямую.

4-й параметр метода combineLatest в Kotlin, компилятор вообще не может определить его тип, поскольку 4-й тип параметра может быть io.reactivex.functions.Function3 или ObservableSource. потому что ObservableSource также является функциональным интерфейсом Java .

Когда вы сталкиваетесь с таким конфликтом SAM Conversion, вы можете используя функцию адаптера, которая преобразует лямбда-выражение в определенный тип SAM, например:

typealias RxFunction3<T1, T2, T3, R> = io.reactivex.functions.Function3<T1,T2,T3,R>

val f: RxFunction3<String, Boolean, Int, Double> = RxFunction3{ s, b, i-> 1.0}

Поэтому вы должны использовать адаптацию перед использованием лямбда / выражения ссылки на функцию, Например:

typealias RxFunction3<T1, T2, T3, R> = io.reactivex.functions.Function3<T1,T2,T3,R>

fun <T1,T2,T3,R> KFunction3<T1,T2,T3,R>.toFunction3(): RxFunction3<T1, T2,T3,R> {
    return RxFunction3 { t1, t2, t3 -> invoke(t1, t2, t3) }
}

Затем вы можете использовать выражение ссылки на функцию как показано ниже, например:

Observable.combineLatest(
        strings(),
        booleans(), 
        integers(), 
        //             v--- convert KFunction3 to RxFunction3 explicitly
        this::combine.toFunction3()
)
person holi-java    schedule 15.07.2017
comment
Спасибо за ответ :). До сегодняшнего дня я не знал, что такая проблема существует. Только что нашел RxKotlin, github.com/ReactiveX/RxKotlin, который решает эту проблему. - person Aleksander Mielczarek; 15.07.2017
comment
@AleksanderMielczarek Вовсе нет. Я тоже сейчас обнаружил проблему в rx-java2. Я никогда не использовал rx-java2, :). - person holi-java; 15.07.2017
comment
Используйте import io.reactivex.rxkotlin.Observables вместо обычного наблюдаемого. Описание класса читает адаптеры SAM, чтобы помочь поддержке лямбда-выражения Kotlin. - person frankelot; 19.02.2018

@ holi-java Ответ отличный, он объясняет, почему возникает эта проблема, здесь я предлагаю быстрое решение для тех, кто просто хочет избавиться от проблемы:

В rxJava2 используйте io.reactivex.rxkotlin.Observables вместо io.reactivex.Observable для объединения ваших наблюдаемых:

Observables.combineLatest(src1, src2, ::yourFunction)

Это должно работать из коробки.

person frankelot    schedule 19.02.2018