parameter - java generics method
Generics, Type Parameters and Wildcards (1)
Estoy tratando de entender los genéricos de Java y parecen extremadamente difíciles de entender. Por ejemplo, esto está bien ...
public class Main {
public static void main(String[] args) {
List<?> list = null;
method(list);
}
public static <T> void method(List<T> list) { }
}
... como es esto ...
public class Main {
public static void main(String[] args) {
List<List<?>> list = null;
method(list);
}
public static <T> void method(List<T> list) { }
}
... y esto ...
public class Main {
public static void main(String[] args) {
List<List<List<?>>> list = null;
method(list);
}
public static <T> void method(List<List<T>> list) { }
}
... pero esto no compila:
public class Main {
public static void main(String[] args) {
List<List<?>> list = null;
method(list);
}
public static <T> void method(List<List<T>> list) { }
}
¿Alguien puede explicar lo que está pasando en un lenguaje simple?
Lo principal para entender con tipos genéricos es que no son covariantes.
Entonces, mientras puedes hacer esto:
final String string = "string";
final Object object = string;
Lo siguiente no compilará:
final List<String> strings = ...
final List<Object> objects = strings;
Esto es para evitar las situaciones en las que elude los tipos genéricos:
final List<String> strings = ...
final List<Object> objects = strings;
objects.add(1);
final String string = strings.get(0); <-- oops
Por lo tanto, repasando tus ejemplos uno por uno
1
Su método genérico toma una List<T>
, pasa en una List<?>
; que es (esencialmente) una List<Object>
. T
se puede asignar al tipo de Object
y el compilador está contento.
2
Su método genérico es el mismo, pasa una List<List<?>>
. T
se puede asignar al tipo List<?>
Y el compilador está feliz de nuevo.
3
Esto es básicamente lo mismo que 2 con otro nivel de anidación. T
sigue siendo el tipo de List<?>
.
4
Aquí es donde va un poco en forma de pera, y donde entra mi punto de arriba.
Su método genérico toma una List<List<T>>
. Usted pasa en una List<List<?>>
. Ahora, como los tipos genéricos no son covariantes, List<?>
No se puede asignar a una List<T>
.
El error real del compilador (Java 8) es:
required:
java.util.List<java.util.List<T>>
found:java.util.List<java.util.List<?>>
reason: no se puede inferir type-variable (s)T
(argumento no coincide;java.util.List<java.util.List<?>>
no se puede convertir ajava.util.List<java.util.List<T>>
)
Básicamente, el compilador le dice que no puede encontrar una T
para asignar debido a que tiene que inferir el tipo de List<T>
anidado en la lista externa.
Veamos esto con un poco más de detalle:
List<?>
Es una List
de algún tipo desconocido , podría ser una List<Integer>
o una List<String>
; podemos get
como Object
, pero no podemos add
. Porque de lo contrario nos encontramos con el problema de covarianza que mencioné.
List<List<?>>
es una List
de List
de algún tipo desconocido; podría ser una List<List<Integer>>
o una List<List<String>>
. En el caso 1 , fue posible asignar T
a Object
y simplemente no permitir add
operaciones en la lista de comodines. En el caso 4 esto no se puede hacer, principalmente porque no hay una construcción de genéricos para evitar add
a la List
externa.
Si el compilador asignara T
a Object
en el segundo caso, sería posible algo como lo siguiente:
final List<List<Integer>> list = ...
final List<List<?>> wildcard = list;
wildcard.add(Arrays.asList("oops"));
Entonces, debido a la covarianza, no es posible asignar una List<List<Integer>>
a ninguna otra List
genérica de forma segura.