Let' say i have this java example:
interface Sink<T> {
void accumulate(T t);
}
public static <T> void drainToSink(Collection<T> collection, Sink<? super T> sink) {
collection.forEach(sink::accumulate);
}
Notice how the second parameter is declared to be ? super T
. I need this because I want to call that method like this:
Sink<Object> sink = .... // some Sink implementation
Collection<String> strings = List.of("abc");
drainToSink(strings, sink);
Now I am trying to achieve the same thing with kotlin (which I have very small experience with):
interface Sink<T> {
fun accumulate(t: T)
}
fun <T> drainToSink(collection: List<T>, sink: Sink<T>) {
....
}
And now I am trying to use it:
fun main(args: Array<String>) {
val sink = object : Sink<Any> {
override fun accumulate(any: Any) { }
}
val strings = emptyList<String>()
drainToSink(strings, sink)
}
Interestingly enough, this does not fail (unless I know too little about kotlin here).
I was really expecting that I need to add to the declaration something like Sink<in T>
to let the compiler know that this is actually just a Consumer
, or is in T
always on, by default?
Can someone who knows kotlin better than me, point me in the right direction?
Like I said in my comment, T
is being inferred as Any
here. That's what I see when letting my IDE add explicit type arguments to the drainToSink
call.
Since kotlin's List
is strictly a producer, because it is immutable, it declares it's type parameter as out E
. You get List<Any>
as a parameter type for drainToSink
, and it's fine to assign a List<String>
to that:
val strings = emptyList<String>()
val x: List<Any> = strings // works
If you change the first parameter type to MutableList<T>
, which does not have a covariant type parameter, your example does fail:
fun <T> drainToSink(collection: MutableList<T>, sink: Sink<T>) {
....
}
val strings = emptyList<String>()
drainToSink(strings, sink) // type mismatch: Required: MutableList<Any>, Found: List<String>