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.