¿Cuáles son las diferencias entre List
, List<?>
, List<T>
, List<E>
, y List<Object>
?
1. Lista
List
: es un tipo sin formato, por lo tanto no typesafe
. Solo generará un error de tiempo de ejecución cuando el casting sea malo. Queremos un error de tiempo de compilación cuando el reparto es malo. No se recomienda su uso.
2. Lista <?>
List<?>
es un comodín ilimitado. ¿Pero no estoy seguro de para qué sirve? Puedo imprimir un List<?>
sin problema:
public static void test(List<?> list){
System.out.println(list); // Works
}
¿Por qué no puedo agregar elementos a un List<?>
?
public static void test(List<?> list){
list.add(new Long(2)); // Error
list.add("2"); // Error
System.out.println(list);
}
3. Lista <T>
public static void test(List<T> list){ // T cannot be resolved
System.out.println(list);
}
No entiendo esta sintaxis. Vi algo como esto, y funciona:
public <T> T[] toArray(T[] a){
return a;
}
A veces, veo <T>
, o <E>
, o <U>
, <T,E>
. ¿Son todos iguales o representan algo diferente?
4. Lista <Objeto>
Esto da el error "El método test(List<Object>)
no es aplicable para el argumento List<String>
":
public static void test(List<Object> list){
System.out.println(list);
}
Si intento esto, obtengo "No se puede transmitir desde List<String>
a List<Object>
":
test((List<Object>) names);
Estoy confundido. String
es una subclase de Object
, entonces ¿por qué no es List<String>
una subclase de List<Object>
?
2
. Escribo algunos códigos para demostrar esto en2
. tyvmList<Object>
?Para la última parte: Aunque String es un subconjunto de Object, List <String> no se hereda de List <Object>.
fuente
List<Object>
?La notación
List<?>
significa "una lista de algo (pero no digo qué)". Dado que el código entest
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 su punto 3) requiere que se declare el parámetro de tipo. La sintaxis de Java para eso es poner
<T>
delante 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
List<Object>
no aceptar aList<String>
, eso tiene sentido porque aString
no lo esObject
; que es una subclase deObject
. La solución es declararpublic static void test(List<? extends Object> set) ...
. Pero entonces elextends Object
es redundante, porque cada clase se extiende directa o indirectamenteObject
.fuente
List<Object>
?List<?>
ya que la lista es de algún tipo específico pero desconocido.List<Object>
realmente sería "una lista de cualquier cosa", ya que de hecho puede contener cualquier cosa.La razón por la que no se puede echar
List<String>
aList<Object>
es que permitiría a violar las restricciones delList<String>
.Piense en el siguiente escenario: si tengo un
List<String>
, se supone que solo contiene objetos de tipoString
. (Que es unafinal
clase)Si puedo convertir eso en a
List<Object>
, entonces eso me permite agregarObject
a esa lista, violando así el contrato original deList<String>
.Por lo tanto, en general, si la clase
C
hereda de la claseP
, no se puede decir queGenericType<C>
también hereda deGenericType<P>
.Nota: ya he comentado sobre esto en una respuesta anterior, pero quería ampliarlo.
fuente
List<Object>
?List<Object>
ya que de alguna manera derrota el propósito de los genéricos. Sin embargo, hay casos en los que el código antiguo puedeList
aceptar diferentes tipos, por lo que es posible que desee actualizar el código para usar la parametrización de tipos solo para evitar advertencias del compilador para los tipos sin formato. (Pero la funcionalidad no ha cambiado)Yo recomendaría leer los rompecabezas de Java. Explica bastante bien la herencia, los genéricos, las abstracciones y los comodines en las declaraciones. http://www.javapuzzlers.com/
fuente
Hablemos de ellos en el contexto de la historia de Java;
List
:Lista significa que puede incluir cualquier objeto. La lista estaba en la versión anterior a Java 5.0; Lista de Java 5.0 introducida, por compatibilidad con versiones anteriores.
List<?>
:?
significa Objeto desconocido, no cualquier Objeto; la?
introducción de comodines es para resolver el problema creado por Generic Type; ver comodines ; pero esto también causa otro problema: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.fuente
En su tercer punto, "T" no se puede resolver porque no se declara, generalmente cuando declara una clase genérica puede usar "T" como el nombre del parámetro de tipo enlazado , muchos ejemplos en línea, incluidos
los tutoriales de Oracle,usan "T" como nombre del parámetro de tipo, por ejemplo, declara una clase como:Estás diciendo que el
FooHandler's
operateOnFoo
método espera una variable de tipo "T" que se declara en la declaración de la clase, con esto en mente, luego puedes agregar otro método comoen todos los casos, T, E o U, todos los identificadores del parámetro de tipo, incluso puede tener más de un parámetro de tipo que utiliza la sintaxis
en su cuarto punto, aunque efectivamente Sting es un subtipo de Objeto, en las clases de genéricos no existe tal relación,
List<String>
no es un subtipo deList<Object>
que son dos tipos diferentes desde el punto de vista del compilador, esto se explica mejor en esta entrada del blogfuente
Teoría
String[]
puede ser lanzado aObject[]
pero
List<String>
No se puede lanzar aList<Object>
.Práctica
Para las listas es más sutil que eso, porque en el momento de la compilación no se verifica el tipo de un parámetro de Lista pasado a un método. La definición del método también podría decir
List<?>
: desde el punto de vista del compilador es equivalente. Es por eso que el ejemplo # 2 del OP da errores de tiempo de ejecución y no errores de compilación.Si maneja un
List<Object>
parámetro pasado a un método con cuidado para no forzar una verificación de tipo en ningún elemento de la lista, entonces puede definir su método usandoList<Object>
pero de hecho aceptar unList<String>
parámetro del código de llamada.R. Entonces, este código no dará errores de compilación o tiempo de ejecución y realmente funcionará (¿y quizás sorprendentemente?):
B. Este código dará un error de tiempo de ejecución:
C. Este código dará un error de tiempo de ejecución (
java.lang.ArrayStoreException: java.util.Collections$UnmodifiableRandomAccessList Object[]
):En B, el parámetro
set
no se escribeList
en tiempo de compilación: el compilador lo ve comoList<?>
. Hay un error de tiempo de ejecución porque, en tiempo de ejecución, seset
convierte en el objeto real pasadomain()
, y eso es aList<String>
. AList<String>
no puede ser lanzado aList<Object>
.En C, el parámetro
set
requiere unObject[]
. No hay error de compilación ni error de tiempo de ejecución cuando se llama con unString[]
objeto como parámetro. Eso es porqueString[]
echa aObject[]
. Pero el objeto real recibido portest()
sigue siendo unString[]
, no cambió. Entonces elparams
objeto también se convierte en aString[]
. Y el elemento 0 de aString[]
no se puede asignar a aLong
!(Espero tener todo aquí, si mi razonamiento es incorrecto, estoy seguro de que la comunidad me lo dirá. ACTUALIZADO: He actualizado el código en el ejemplo A para que realmente se compile, sin dejar de mostrar el punto hecho).
fuente
List<Object> cannot be applied to List<String>
. No puede pasarArrayList<String>
a un método que esperaArrayList<Object>
.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 cumplidor llamará a List.toString ();
Problema 3: estos símbolos son los mismos, pero puede darles especificaciones diferentes. Por ejemplo:
Problema 4: la colección no permite la covarianza de parámetros de tipo. Pero la matriz permite la covarianza.
fuente
Tienes razón: String es un subconjunto de Object. Como String es más "preciso" que Object, debe convertirlo para usarlo como argumento para System.out.println ().
fuente