4

假设我有这个 java 示例:

interface Sink<T> {
    void accumulate(T t);
}

public static <T> void drainToSink(Collection<T> collection, Sink<? super T> sink) {
    collection.forEach(sink::accumulate);
}

注意第二个参数是如何声明的? super T。我需要这个,因为我想这样调用该方法:

Sink<Object> sink = .... // some Sink implementation
Collection<String> strings = List.of("abc");
drainToSink(strings, sink);

现在我正试图用 kotlin 来实现同样的目标(我对它的经验很少):

interface Sink<T> {
    fun accumulate(t: T)
}

fun <T> drainToSink(collection: List<T>, sink: Sink<T>) {
   ....
}

现在我正在尝试使用它:

fun main(args: Array<String>) {

     val sink = object : Sink<Any> {
         override fun accumulate(any: Any) {  }
     }

     val strings = emptyList<String>()
     drainToSink(strings, sink)
}

有趣的是,这并没有失败(除非我在这里对 kotlin 知之甚少)。

我真的希望我需要在声明中添加一些东西,比如Sink<in T>让编译器知道这实际上只是一个Consumer,或者in T默认情况下总是打开?

比我更了解 kotlin 的人能指出我正确的方向吗?

4

1 回答 1

3

就像我在评论中所说的那样,T被推断为Any这里。这就是我在让我的 IDE 向drainToSink调用添加显式类型参数时看到的。

由于 kotlinList严格来说是一个生产者,因为它是不可变的,所以它声明它的类型参数为out E. 您将List<Any>作为参数类型获取drainToSink,并且可以将 a 分配List<String>给它:

val strings = emptyList<String>()
val x: List<Any> = strings // works

如果您将第一个参数类型更改为MutableList<T>没有协变类型参数的 ,您的示例确实会失败:

fun <T> drainToSink(collection: MutableList<T>, sink: Sink<T>) {
    ....
}
val strings = emptyList<String>()
drainToSink(strings, sink) // type mismatch: Required: MutableList<Any>, Found: List<String>
于 2018-09-05T20:06:38.527 回答