1
votes

I had the following Java code:

public class DatabaseManager {
    public interface Predicate<T> {
        boolean test(T t);
    }

    private interface Consumer<T> {
        void apply(T t);
    }

    private void updateLiveLists() {
        updateLiveLists(liveList -> true);
    }

    private void updateLiveLists(Predicate<MutableLiveList<?>> predicate) {
        forEachLiveList(liveList -> {
            if (predicate.test(liveList)) {
                refresh(liveList);
            }
        });
    }

    private void forEachLiveList(Consumer<MutableLiveList<?>> consumer) {
        ...
    }

Then I used Java -> Kotlin conversion in Android Studio:

class DatabaseManager {
    interface Predicate<T> {
        fun test(t: T): Boolean
    }

    private interface Consumer<T> {
        fun apply(t: T)
    }

    private fun updateLiveLists(predicate: Predicate<MutableLiveList<*>> = { liveList -> true }) {
        forEachLiveList({ liveList ->
            if (predicate.test(liveList)) {
                refresh(liveList)
            }
        })
    }

    private fun forEachLiveList(consumer: Consumer<MutableLiveList<*>>) {
       ...
    }

Fails with the following error:

Type mismatch

Required: DatabaseManager.Consumer<MutableLiveList<*>>

Found: (???) -> Unit

Now I had to change code to following:

private fun updateLiveLists(predicate: Predicate<MutableLiveList<*>> = object : Predicate<MutableLiveList<*>> {
    override fun test(t: MutableLiveList<*>): Boolean {
        return true;
    }
}) {
    forEachLiveList(object : DatabaseManager.Consumer<MutableLiveList<*>> { // <--- !!
        override fun apply(t: MutableLiveList<*>) {
            if (predicate.test(t)) {
                refresh(t)
            }
        }
    })
}

Okay, so I had to explicitly declare this anonymous interface as an explicit subclass of object, as for whatever reason Kotlin couldn't figure out the lambda.


If it helps, I have the same problem occurring in a function below it:

fun refresh(vararg tables: Table) {
    updateLiveLists({ liveList ->
        for (table in tables) {
            if (liveList.getTable() === table) {
                return@updateLiveLists true
            }
        }
        false
    })
}

Which says:

Type Mismatch:

Required: DatabaseManager.Predicate<MutableLiveList<*>>

Found: ??? -> Boolean

And I have to do this instead

fun refresh(vararg tables: Table) {
    updateLiveLists(object: DatabaseManager.Predicate<MutableLiveList<*>> { // <--
        override fun test(t: MutableLiveList<*>): Boolean {
            for (table in tables) {
                if (t.getTable() === table) {
                    return true
                }
            }
            return false
        }
    })
}

Why, and how can I avoid this? How can I use my own Predicate/Consumer without Kotlin getting confused about the lambda type?

1
It's the most shortest question as I seen today)Dima Kozhevin
I think here you can find answer stackoverflow.com/questions/43235423/…Jemo Mgebrishvili
@DimaKozhevin it is kinda long because I posted 2 examples in which the failure occurs.EpicPandaForce
@JemoMgebrishvili so it says I either need to use object : as I did above, or change these interfaces to typealias of Kotlin's function types, or use Kotlin's function types directly? Changing to typealias or to Kotlin types would work only if I change all code that uses this to Kotlin as well. Hrmm... Yes, that is a very relevant question.EpicPandaForce
@EpicPandaForce, or you could leave functional interfaces in Java and implement them in Kotlin. At least this I think is hinted at in linked answer.M. Prokhorov

1 Answers

1
votes

Thanks to /u/lupajz I now know that the problem is that interfaces defined in Kotlin are not converted by the SAM conversion because of https://discuss.kotlinlang.org/t/kotlin-and-sam-interface-with-two-parameters/293/5

Basically it boils down to

"why would you do it this way when you can use Kotlin's functional interfaces and type-aliases instead; if you need this then define the interfaces in Java".


There are a few workarounds:

1.) inline objects (which is what I showed above as part of the question)

2.) type aliases + exposing overloaded methods

private typealias KotlinPredicate<T> = (T) -> Boolean;

private typealias KotlinConsumer<T> = (T) -> Unit;

class DatabaseManager {
    private interface Consumer<T> {
        fun apply(t : T) : Unit;
    }

    private fun forEachLiveList(consumer: Consumer<MutableLiveList<*>>) {
        forEachLiveList({
            consumer.apply(it)
        })
    }

    private fun forEachLiveList(consumer: KotlinConsumer<MutableLiveList<*>>) {
        ...
    }

and

interface Predicate<T> {
    fun test(t : T) : Boolean;
}


fun updateLiveLists(predicate: Predicate<MutableLiveList<*>>) {
    updateLiveLists({
        predicate.test(it)
    })
}

fun updateLiveLists(predicate: KotlinPredicate<MutableLiveList<*>> = { liveList -> true }) {
    forEachLiveList({ liveList ->
        if (predicate.invoke(liveList)) {
            refresh(liveList)
        }
    })
}