java - recorrer - Diferencia entre Lista, Lista<?>, Lista<T>, Lista<E> y Lista<Objeto>
recorrer lista de objetos java (10)
1) Correcto
2) Puede pensar en esa como una lista de "solo lectura", donde no le importa el tipo de elementos. Podría, por ejemplo, ser utilizado por un método que devuelve la longitud de la lista.
3) T, E y U son lo mismo, pero las personas tienden a usar, por ejemplo, T para tipo, E para Elemento, V para valor y K para clave. El método que compila dice que tomó una matriz de cierto tipo y devuelve una matriz del mismo tipo.
4) No puedes mezclar naranjas y manzanas. Usted podría agregar un Objeto a su lista de Cadenas si pudiera pasar una lista de cadenas a un método que espera listas de objetos. (Y no todos los objetos son cadenas)
¿Cuáles son las diferencias entre List
, List<?>
, List<T>
, List<E>
y List<Object>
?
Ahora no hago esta pregunta a ciegas, así que por favor no cierren este hilo. Déjame primero introducir el código base:
private static List<String> names = new ArrayList<String>();
static {
names.add("Tom");
names.add("Peter");
names.add("Michael");
names.add("Johnson");
names.add("Vlissides");
}
public static void test(List<String> set){
System.out.println(set);
}
public static void main(String args[]){
test(names);
}
Yo entiendo eso:
1. List
: es un tipo sin procesar, por lo tanto, no es typesafe
. Solo generará un error de tiempo de ejecución cuando la conversión sea incorrecta. Queremos un error de tiempo de compilación cuando el lanzamiento es malo. No recomendado para usar.
2. List<?>
: Es un comodín ilimitado. Pero no estoy seguro de para qué sirve esto? Entonces si cambio el método de test
a
public static void test(List<?> set){
System.out.println(set);
}
todavía funciona bien Si puede explicar el uso de esto, lo agradecería enormemente.
EDITAR : Si hago esto:
public static void test(List<?> set){
set.add(new Long(2)); //--> Error
set.add("2"); //--> Error
System.out.println(set);
}
pero si cambio la test
a esto:
public static void test(List<String> set){
set.add(new Long(2)); //--> Error
set.add("2"); //--> Work
System.out.println(set);
}
3. List<T>
:
public static void test(List<T> set){ //T cannot be resolved
System.out.println(set);
}
Supongo que no entiendo esta sintaxis. Vi algo así y funciona:
public <T> T[] toArray(T[] a){
return a;
}
Por favor, explícame esto por favor? A veces, veo <T>
, o <E>
, o <U>
, <T,E>
. ¿Son todos iguales o representan algo diferente?
4. List<Object>
public static void test(List<Object> set){
System.out.println(set);
}
Luego recibí el error. The method test(List<Object>) is not application for the argument List<String>
para el siguiente código. Estoy confundido. Pensé que String
era un subconjunto de Object
?
public static void main(String args[]){
test(names);
}
EDITAR: Si pruebo esto
test((List<Object>)names);
luego obtuve Cannot cast from List<String> to List<Object>
Aconsejaría leer rompecabezas de Java. Explica bastante bien la herencia, los genéricos, las abstracciones y los comodines en las declaraciones. http://www.javapuzzlers.com/
El problema 2 está bien, porque "System.out.println (set);" significa "System.out.println (set.toString ());" set es una instancia de List, por lo que el compilador llamará a List.toString ();
public static void test(List<?> set){
set.add(new Long(2)); //--> Error
set.add("2"); //--> Error
System.out.println(set);
}
Element ? will not promise Long and String, so complier will not accept Long and String Object
public static void test(List<String> set){
set.add(new Long(2)); //--> Error
set.add("2"); //--> Work
System.out.println(set);
}
Element String promise it a String, so complier will accept String Object
Problema 3: estos símbolos son los mismos, pero puedes darles la especificación de differet. Por ejemplo:
public <T extends Integer,E extends String> void p(T t, E e) {}
Problema 4: La recopilación no permite la covarianza del parámetro de tipo. Pero la matriz permite la covarianza.
En su tercer punto, "T" no puede ser resuelto porque no está declarado, generalmente cuando declara una clase genérica puede usar "T" como el nombre del parámetro de tipo encuadernado , muchos ejemplos en línea que incluyen los tutoriales de Oracle usan "T" como el nombre del parámetro tipo, digamos por ejemplo, declaras una clase como:
public class FooHandler<T>
{
public void operateOnFoo(T foo) { /*some foo handling code here*/}
}
usted está diciendo que FooHandler''s
método FooHandler''s
operateOnFoo
espera una variable de tipo "T" que se declara en la declaración de clase en sí, con esto en mente, puede agregar más tarde otro método como
public void operateOnFoos(List<T> foos)
en todos los casos, ya sea T, E o U, todos los identificadores del parámetro tipo, incluso puede tener más de un parámetro de tipo que utiliza la sintaxis
public class MyClass<Atype,AnotherType> {}
en su cuarta ponint aunque efectivamente Sting es un sub tipo de Object, en las clases de genéricos no existe tal relación, List<String>
no es un subtipo de List<Object>
son dos tipos diferentes desde el punto de vista del compilador, esto se explica mejor en esta entrada de blog
Hablemos de ellos en el contexto de la historia de Java;
-
List
:
La lista significa que puede incluir cualquier Objeto. La lista estaba en la versión anterior a Java 5.0; Java 5.0 presentó la Lista, por compatibilidad con versiones anteriores.
List list=new ArrayList();
list.add(anyObject);
-
List<?>
:
?
significa Objeto desconocido, no cualquier Objeto; el comodín ?
la introducción es para resolver el problema creado por Generic Type; ver wildcards ; pero esto también causa otro problema:
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
-
List< T> List< E>
Significa declaración genérica en la premisa de ningún tipo T o E en su proyecto Lib.
-
List< Object>
significa parametrización genérica.
La notación List<?>
Significa "una lista de algo (pero no digo qué)". Como el código en test
funciona para cualquier tipo de objeto en la lista, esto funciona como un parámetro de método formal.
El uso de un parámetro de tipo (como en el punto 3) requiere que se declare el parámetro de tipo. La sintaxis de Java para eso es poner <T>
al frente de la función. Esto es exactamente análogo a declarar nombres de parámetros formales a un método antes de usar los nombres en el cuerpo del método.
En cuanto a la List<Object>
no acepta una List<String>
, tiene sentido porque una String
no es un Object
; es una subclase de Object
. La solución es declarar public static void test(List<? extends Object> set) ...
Pero luego el extends Object
es redundante, porque cada clase extiende directa o indirectamente el Object
.
La razón por la que no puede convertir List<String>
en List<Object>
es que le permitirá violar las restricciones de List<String>
.
Piensa en el siguiente escenario: si tengo una List<String>
, se supone que solo debe contener objetos de tipo String
. (Que es una clase final
)
Si puedo convertirlo en una List<Object>
, entonces eso me permite agregar Object
a esa lista, violando así el contrato original de List<String>
.
Por lo tanto, en general, si la clase C
hereda de la clase P
, no se puede decir que GenericType<C>
también hereda de GenericType<P>
.
NB Ya he comentado esto en una respuesta anterior, pero quería ampliarlo.
Para la última parte: Aunque String es un subconjunto de Object, pero List <String> no se hereda de List <Object>.
Tienes razón: String es un subconjunto de Object. Dado que String es más "preciso" que Object, debe convertirlo para usarlo como argumento para System.out.println ().
Teoría
String[]
se puede convertir a Object[]
pero
List<String>
no se puede convertir a List<Object>
.
Práctica
Para las listas es más sutil que eso, porque en tiempo de compilación, el tipo de un parámetro List pasado a un método no está marcado. La definición de método también podría decir List<?>
: Desde el punto de vista del compilador es equivalente. Esta es la razón por la cual el ejemplo # 2 del PO da errores de tiempo de ejecución no compila errores.
Si maneja un parámetro List<Object>
pasado a un método cuidadosamente para no forzar una verificación de tipo en ningún elemento de la lista, entonces puede hacer que su método se defina usando List<Object>
pero de hecho acepte una List<String>
parámetro del código de llamada.
A. Entonces, este código no dará errores de compilación o tiempo de ejecución y realmente (¿y quizás sorprendentemente?) Funcionará:
public static void test(List<Object> set) {
List<Object> params = new List<>(); // This is a List<Object>
params.addAll(set); // A String can be added to List<Object>
params.add(new Long(2)); // A Long can be added to List<Object>
System.out.println(params);
}
public static void main(String[] args) {
List<String> argsList = Arrays.asList(args);
test(argsList); // The object passed is a List<String>
}
B. Este código dará un error de tiempo de ejecución:
public static void test(List<Object> set) {
List<Object> params = set; // Surprise! Runtime error
set.add(new Long(2)); // Also a runtime error
System.out.println(set);
}
public static void main(String[] args) {
List<String> argsList = Arrays.asList(args);
test(argsList);
}
C. Este código dará un error de tiempo de ejecución ( java.lang.ArrayStoreException: java.util.Collections$UnmodifiableRandomAccessList Object[]
):
public static void test(Object[] set) {
Object[] params = set; // This is OK even at runtime
params[0] = new Long(2); // Surprise! Runtime error
System.out.println(params);
}
public static void main(String[] args) {
test(args);
}
En B, el set
parámetros no es una List
tipeada en tiempo de compilación: el compilador la ve como List<?>
. Hay un error de tiempo de ejecución porque en el tiempo de ejecución, set
convierte en el objeto real pasado de main()
, y eso es una List<String>
. Una List<String>
no se puede convertir a List<Object>
.
En C, el set
parámetros requiere un Object[]
. No hay ningún error de compilación y ningún error de tiempo de ejecución cuando se llama con un objeto String[]
como parámetro. Eso es porque String[]
lanza a Object[]
. Pero el objeto real recibido por test()
sigue siendo String[]
, no cambió. Entonces el objeto params
también se convierte en String[]
. ¡Y el elemento 0 de una String[]
no se puede asignar a un Long
!
(Espero tener todo aquí, si mi razonamiento es incorrecto, estoy seguro de que la comunidad me lo dirá).