1

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?

8
  • 1
    It's the most shortest question as I seen today) Commented Oct 13, 2017 at 11:50
  • I think here you can find answer stackoverflow.com/questions/43235423/… Commented Oct 13, 2017 at 11:50
  • 2
    @DimaKozhevin it is kinda long because I posted 2 examples in which the failure occurs. Commented Oct 13, 2017 at 12:00
  • @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. Commented Oct 13, 2017 at 12:02
  • 2
    @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. Commented Oct 13, 2017 at 12:06

1 Answer 1

1

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)
        }
    })
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.