Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Behavior with Kotlin Higher-Order Functions and Single-method interfaces?

I was having some issues using RxJava and Kotlin earlier. I made some interesting discoveries which I'm still puzzled about.

There is the simple Func1 interface in RxJava

public interface Func1<T, R> extends Function {
    R call(T t);
}

I was trying to add an extension method to an Observable, also an RxJava class. This would collect the emissions into a Google Guava ImmutableListMulitmap using a Func1 to map the key off each item.

fun <K,T> Observable<T>.toImmutableListMultimap(keyMapper: Func1<T, K>): Observable<ImmutableListMultimap<K,T>> {
    return this.collect({ ImmutableListMultimap.builder<K,T>()},{ b, t -> b.put(keyMapper.call(t), t)}).map { it.build() }
}

When I tried to invoke this extension method I could not get it to compile, and it was not understanding the lambda expression at all.

ScheduledItem.all.flatMap { it.rebuildSoftTransactions }
.toImmutableListMultimap { it.id /*compile error */ } .cache()

However, the strangest thing happened when I modified the extension method to use the function type.

fun <K,T> Observable<T>.toImmutableListMultimap(keyMapper: (T) -> K): Observable<ImmutableListMultimap<K,T>> {
    return this.collect({ ImmutableListMultimap.builder<K,T>()},{ b, t -> b.put(keyMapper(t), t)}).map { it.build() }
}

And then everything compiled fine. But this is what puzzled me: How come it did not infer the lambda onto the interface? When I use the standard map() method on the Observable it infers the lambda just fine using the curly bracket { } syntax. But why does it not work for my extension method above?

like image 425
tmn Avatar asked Jan 04 '16 01:01

tmn


1 Answers

The SAM conversion (converting a lambda into a function type) currently works only for methods written in Java. Kotlin has proper function types, so there is no need for SAM conversion - you can declare the parameter as a function type directly (which works, as you have observed).

Observable.map() is written in Java, so the SAM conversion is applied. Your extension function is written in Kotlin, so it's not.

like image 132
yole Avatar answered Oct 09 '22 22:10

yole