4

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?

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • Isn't `T` just being inferred to `Any` here? And since you have [`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html) that also works for the first parameter? – Jorn Vernee Sep 05 '18 at 19:43
  • @JornVernee seems to be right - Kotlin's `List` is indeed [`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html), as opposed to its [`MutableList`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-list/index.html). – Tomasz Linkowski Sep 05 '18 at 19:48
  • @TomaszLinkowski oh shoot! so that turns out to become `List extends T>` sort of... well sounds like an answer to me – Eugene Sep 05 '18 at 19:53
  • @JornVernee you're right... – Eugene Sep 05 '18 at 19:53
  • `Sink` should probably just be declared as `Sink`... – Louis Wasserman Sep 05 '18 at 22:28
  • @LouisWasserman that is correct *in general*, my question was why *not* declaring it still makes it work – Eugene Sep 06 '18 at 08:32

1 Answers1

3

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>
Jorn Vernee
  • 31,735
  • 4
  • 76
  • 93
  • well that is interesting, so in java words that method would become `public static void drainToSink(List extends T> collection, Sink sink) {` (because of that `out`), so yes I can see that `T` would be inferred to `Any`. – Eugene Sep 06 '18 at 12:38
  • Now I have a problem understanding this `val strings = emptyList() val x: List = strings // works` or to be more exact what is the translation of this to java... well `emptyList` would be `List extends String>`, but what about `List`? `List>` or `List extends Object>`, because as far as I understand it can't be `List`... – Eugene Sep 06 '18 at 12:39
  • @Eugene Yeah, you need a wildcard when translating `List` to Java. I guess technically it would become `List extends Any>`, but since Java doesn't have `Any`, the closest is probably `List extends Object>` which is more or less the same as `List>`. And indeed, `List> l = (List extends String>) new ArrayList();` also works fine. – Jorn Vernee Sep 06 '18 at 14:03
  • well if we "translate" `List` to `List>`, how would I translate `List<*>`? :) this is slightly eating my brain in mysterious ways – Eugene Sep 06 '18 at 14:13
  • @Eugene Also to `List>` I'd say. – Jorn Vernee Sep 06 '18 at 14:26