Дженерики kotlin только для потребителей
Давайте скажем, у меня есть этот пример 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 лучше, чем я, указать мне в правильном направлении?
1 ответ
Как я уже сказал в своем комментарии, T
выводится как Any
Вот. Это то, что я вижу, когда позволяю моей IDE добавлять явные аргументы типа к drainToSink
вызов.
С котлина List
строго производитель, потому что он неизменен, он объявляет свой параметр типа как out E
, Ты получаешь List<Any>
в качестве типа параметра для drainToSink
и можно назначить 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>