Vamos a decir que tengo este ejemplo de Java:
interface Sink<T> {
void accumulate(T t);
}
public static <T> void drainToSink(Collection<T> collection, Sink<? super T> sink) {
collection.forEach(sink::accumulate);
}
Nótese como el segundo parámetro se declara ? super T
. Necesito esto porque quiero llamar a ese método como este:
Sink<Object> sink = .... // some Sink implementation
Collection<String> strings = List.of("abc");
drainToSink(strings, sink);
Ahora estoy tratando de lograr lo mismo con Kotlin (que tengo muy poca experiencia con):
interface Sink<T> {
fun accumulate(t: T)
}
fun <T> drainToSink(collection: List<T>, sink: Sink<T>) {
....
}
Y ahora que estoy tratando de usarlo:
fun main(args: Array<String>) {
val sink = object : Sink<Any> {
override fun accumulate(any: Any) { }
}
val strings = emptyList<String>()
drainToSink(strings, sink)
}
Curiosamente, esto no falla (a menos que sepa muy poco sobre Kotlin aquí).
Me esperaba que tengo que añadir a la declaración algo así como Sink<in T>
dejar que el compilador sabe que esto es realmente solo una Consumer
, o es in T
siempre, de manera predeterminada?
¿Puede alguien que conoce Kotlin mejor que yo, me punto en la dirección correcta?
Como dije en mi comentario, T
está siendo inferido como Any
aquí. Eso es lo que veo cuando dejando que mi IDE añadir argumentos de tipo explícitas a la drainToSink
llamada.
Desde Kotlin de List
es estrictamente un productor, porque es inmutable, que declara que es el parámetro tipo que out E
. Se obtiene List<Any>
como un tipo de parámetro para drainToSink
, y está bien asignar una List<String>
a lo siguiente:
val strings = emptyList<String>()
val x: List<Any> = strings // works
Si cambia el primer tipo de parámetro a MutableList<T>
, que no tiene un parámetro de tipo covariante, su ejemplo no falte;
fun <T> drainToSink(collection: MutableList<T>, sink: Sink<T>) {
....
}
val strings = emptyList<String>()
drainToSink(strings, sink) // type mismatch: Required: MutableList<Any>, Found: List<String>