Why cannot Kotlin infer the following lambda argument (after Java -> Kotlin conversion)? - java

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?

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)
}
})
}

Related

Hot observable with filters RX Java

I am trying to create a hot observable with filtering in register method as list of events that are implementing a base interface as below:
public void register(Observer<AppEvent> observer, List<AppEvent> filter) {
Log.d(TAG, "Registering observer");
subject.subscribeOn(Schedulers.io())
.filter(o -> {
return filter.stream().anyMatch(it -> (o instanceof it));
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
public interface AppEvent extends Serializable {
}
public static class ItemDetails implements AppEvent {
public String info;
public ItemDetails(String malwareInfo) {
info = malwareInfo;
}
}
public static class InitApi implements AppEvent {
public Boolean isSuccess;
public InitApi(boolean isSuccess) {
this.isSuccess = isSuccess;
}
}
What I am trying to do is to have a filter based on type of event class, in our case ItemDetails or InitApi.
Logically the code is correct:
.filter(o -> {
return filter.stream().anyMatch(it -> (o instanceof it));
})
but the compiler says unknown class it.
How can I do filtering over a list of AppEvent?
As mentioned in the comments, you're seeing the compiler error because the instanceof operator requires the right-hand-side argument to be either a class or an interface.
If you're trying to create an "allowed" list of events to pass on to the Observer, you could do something like this:
public void register(Observer<AppEvent> observer, List<AppEvent> filter) {
Log.d(TAG, "Registering observer");
// Collect the names of events to pass on to the observer
final List<String> allowedEventNames = filter.stream()
.map(it -> it.getClass().getName())
.collect(Collectors.toList());
subject.subscribeOn(Schedulers.io())
// Filter based on names
.filter(event -> allowedEventNames.contains(event.getClass().getName()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}

Android - Observation method did not work

The observer I created is not working. There are classes that I have run before, but the current one did not work. I could not understand where the problem was. The observation methods I created before are working.
SingleLiveData.kt
class SingleLiveData<T> : MutableLiveData<T>() {
private val pending = AtomicBoolean()
#MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
if (hasActiveObservers()) {
Timber.w("Multiple observers registered but only one will be notified of changes.")
}
super.observe(owner, Observer { t ->
if (pending.compareAndSet(true, false)) {
observer.onChanged(t)
}
})
}
#MainThread
override fun setValue(value: T?) {
pending.set(true)
super.setValue(value)
}
}
ViewModel.kt
class ExampleViewModel#Inject constructor(): ViewModel() {
val event = SingleLiveData<ExampleViewEvent>()
fun goToHome(userId: Long) {
event.postValue(ExampleViewEvent.GoToHome(userId))
}
}
Fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
observe(viewModel.event, ::onViewEvent)
}
private fun onViewEvent(viewEvent: ExampleViewEvent) {
when (viewEvent) {
is ExampleViewEvent.GoToHome ->
findNavController().navigate(
ExampleFragmentDirections
.actionExampleFragmentToHomeFragment(viewEvent.id))
}
}
EDIT:
observe() Method:
fun <T> LifecycleOwner.observe(liveData: LiveData<T>, observer: (T) -> Unit) {
liveData.observe(this, Observer {
it?.let { t -> observer(t) }
})
}
fun <T> LifecycleOwner.observe(liveData: MutableLiveData<T>, observer: (T) -> Unit) {
liveData.observe(this, Observer {
it?.let { t -> observer(t) }
})
}
NOTE: hasActiveObservers() return false.

Call Kotlin function with Lambda parameters in Java

How do I call Kotlin functions with lambdas in the parameter from a Java class.
Example
fun getToken(
tokenUrl: String,
onSuccess: (String) -> Unit,
onError: (Error) -> Unit
): Provider {
//Implementation
}
You can do this
value.getToken("url",
new Function1<String, Unit>() {
#Override
public Unit invoke(String s) {
/* TODO */
return Unit.INSTANCE;
}
}, new Function1<Throwable, Unit>() {
#Override
public Unit invoke(Throwable throwable) {
/* TODO */
return Unit.INSTANCE;
}
});
You can call it with normal Java 8 lambdas, since Kotlin is actually using a single-method interface on the backend.
myFoo.getToken(tokenUrl, successString -> { ... }, error -> { ... });
This is for calling it within Kotlin, the question was edited to say "from Java" after I provided my answer. Leaving it here anyway in hope that it is useful.
You can call it like this
fun test() {
getToken("asdf", { print(it) }, { println(it) })
getToken("asdf", { print(it) }) { err -> println(err) }
getToken("asdf", ::println, ::println)
getToken("asdf", ::println) { println(it) }
}

Kotlin vs Java nested generics

I'm having some trouble when trying to translate Java code with nested generics to Kotlin. Take this Java SSCCE as an example (please note the relation between S and T):
public class JavaTest {
private class JavaObjectContainer<S> {
public S obj;
}
private abstract class JavaSampleClass<S, T extends JavaObjectContainer<S>> {
private Class<S> type;
public JavaSampleClass(Class<S> type) {
this.type = type;
}
public Class<S> getType() {
return type;
}
public abstract void callMethod(S s);
}
private class JavaChildSampleClass extends JavaSampleClass<String, JavaObjectContainer<String>> {
public JavaChildSampleClass() {
super(String.class);
}
#Override
public void callMethod(String s) {}
}
private class JavaTestContainer {
private Map<Class<?>, JavaSampleClass> sampleClasses;
public JavaTestContainer() {
this.sampleClasses = new HashMap<>();
}
public void registerJavaSampleClass(JavaSampleClass javaSampleClass) {
sampleClasses.put(javaSampleClass.getType(), javaSampleClass);
}
public void callMethod(Object obj) {
sampleClasses.get(obj.getClass()).callMethod(obj);
}
}
public void test() {
JavaTestContainer javaTestContainer = new JavaTestContainer();
javaTestContainer.registerJavaSampleClass(new JavaChildSampleClass());
javaTestContainer.callMethod("Hola");
}
}
Think of this SSCCE as an implementation of a generic factory pattern, where the user registers multiple JavaSampleClass whose methods can be invoked in the future.
As Kotlin does not provide an alternative to wildcards, I have tried the following approach:
class KotlinTest {
private class KotlinObjectContainer<S> {
var obj : S? = null
}
private open class KotlinSampleClass<S, T : KotlinObjectContainer<S>>(var type: Class<S>) {
fun callMethod(s : S) {}
}
private class KotlinChildSampleClass : KotlinSampleClass<String, KotlinObjectContainer<String>>(String::class.java)
private inner class KotlinTestContainer {
private val sampleClasses: MutableMap<Class<Any>, KotlinSampleClass<Any, KotlinObjectContainer<Any>>> = mutableMapOf()
fun registerKotlinSampleClass(kotlinSampleClass: KotlinSampleClass<Any, KotlinObjectContainer<Any>>) {
sampleClasses.put(kotlinSampleClass.type, kotlinSampleClass)
}
fun callMethod(obj : Any) {
sampleClasses[obj.javaClass]?.callMethod(obj)
}
}
fun test() {
val kotlinTestContainer = KotlinTestContainer()
// Exception!
kotlinTestContainer.registerKotlinSampleClass(KotlinChildSampleClass())
kotlinTestContainer.callMethod("Hello")
}
}
The above code throws the following exception in the IDE:
Type mismatch.
Required: KotlinTest.KotlinSampleClass<Any, KotlinObjectContainer<Any>>
Found: KotlinTest.KotlinChildSampleClass
I have been thinking of declaring sampleClasses map as
MutableMap<*, *>
But then, how can I initialize it? Also, as * represents an out-projected parameter, the IDE shows me an error when trying to put new values in the map.
How can I overcome this issue? I'm quite certain that I'm missing something...
As Kotlin does not provide an alternative to wildcards...
I have been thinking of declaring sampleClasses map as
MutableMap<*, *>
For this case * corresponds to wildcards perfectly well. If you have Class<?> in Java, you want Class<*> in Kotlin, not Class<Any> or *.
private val sampleClasses: MutableMap<Class<*>, KotlinSampleClass<*, *>> = mutableMapOf()
fun registerKotlinSampleClass(kotlinSampleClass: KotlinSampleClass<*, *>) {
sampleClasses.put(kotlinSampleClass.type, kotlinSampleClass)
}
#Suppress("UNCHECKED_CAST")
fun callMethod(obj : Any) {
(sampleClasses[obj.javaClass] as KotlinSampleClass<Any, *>?)?.callMethod(obj)
}
The only reason you don't need the cast in callMethod in Java is because you are using raw types (as Turing85's comment mentions) and the compiler basically gives up on type checking.

Strange behavior of compose and ObservableTransformer in RxJava when the passed generic extends something

While trying to play with compose in RxJava2 I found something I can't really explain.
Suppose we have the following two static methods:
object RxUtils {
fun <T> handleResponse(): ObservableTransformer<T, T> {
return ObservableTransformer { observable ->
observable.flatMap { t ->
ObservableFromCallable.fromCallable(object: Callable<T>{
override fun call(): T {
return t
}
})
}
}
}
fun <T: GenericResponse> handleResponse2(): ObservableTransformer<T, T> {
return ObservableTransformer { observable ->
observable.flatMap { t ->
ObservableFromCallable.fromCallable(object: Callable<T>{
override fun call(): T {
return t
}
})
}
}
}}
handleResponse and handleResponse2 are the same, the difference is that in handleResponse2 T extends GenericResponse.
The problem is that when i use handleResponse2 in compose, the object which comes from compose is not an Observable and I can't use any Observable operators.
Example:
fun <T: GenericResponse> loadApi2(observable: Observable<T>): Observable<T> {
return observable
.compose(RxUtils.handleResponse2()) // here is blocked, can't use any other operator
}
fun <T: GenericResponse> loadApi(observable: Observable<T>): Observable<T> {
return observable
.compose(RxUtils.handleResponse()) //works fine, I can use other operators such as compose, subscribeOn etc
}

Categories