metodos - listas en java
¿Cuál es la diferencia entre los tipos sin formato, los comodines ilimitados y el uso de Object en genéricos? (8)
Estoy leyendo el capítulo sobre Genéricos en Java Efectivo.
Ayudame a entender la diferencia entre Set
, Set<?>
Y Set<Object>
?
El siguiente párrafo está tomado del libro.
Como una revisión rápida,
Set<Object>
es un tipo parametrizado que representa un conjunto que puede contener objetos de cualquier tipo,Set<?>
Es un tipo comodín que representa un conjunto que puede contener solo objetos de algún tipo desconocido, ySet
es un raw tipo, que opta por el sistema de tipo genérico.
¿Qué se entiende por "algún tipo desconocido"? ¿Son todos tipos desconocidos de tipo Object
? En ese caso, ¿cuál es la diferencia específica entre Set<?>
Y Set<Object>
?
qué se entiende por "algún tipo desconocido"
Exactamente lo que significa: el Set
tiene un parámetro genérico, pero no sabemos de qué se trata.
Entonces, el conjunto asignado a una variable Set<?>
Puede ser un Set<String>
, o un Set<Integer>
, o un Set<Map<Integer, Employee>>
o un conjunto que contenga cualquier otro tipo específico.
Entonces, ¿qué significa eso para cómo puedes usarlo? Bueno, cualquier cosa que obtengas será una instancia de ?
, sea lo que sea. Como no sabemos cuál es el parámetro de tipo, no podemos decir nada más específico que que los elementos del conjunto se puedan asignar a Object
(solo porque todas las clases se extienden desde él).
Y si estás pensando en agregar algo al conjunto, bueno, ¿el método add
toma un ?
(lo cual tiene sentido, ya que este es el tipo de objetos dentro del conjunto). Pero si intenta agregar un objeto específico, ¿cómo puede estar seguro de que es seguro? No se puede: si está insertando un String, podría ponerlo en un Set<Integer>
por ejemplo, lo que rompería la seguridad de tipo que obtiene de los genéricos. Por lo tanto, aunque no conozca el tipo del parámetro genérico, no puede proporcionar ningún argumento de este tipo (con la única excepción de null
, ya que es una "instancia" de cualquier tipo).
Al igual que con la mayoría de las respuestas relacionadas con los genéricos, esto se ha centrado en las colecciones porque son más fáciles de comprender instintivamente. Sin embargo, los argumentos se aplican a cualquier clase que toma parámetros genéricos, si se declara con el parámetro wildcard ilimitado ?
, no puede proporcionarle ningún argumento, y cualquier valor que reciba de ese tipo solo se podrá asignar a Object
.
Digamos que está escribiendo un método común para imprimir los elementos que aparecen en una lista. Ahora, este método podría usarse para imprimir listas de tipo entero, doble, objeto o cualquier otro tipo. Cuál eliges?
List<Object>
: si usamos este, esto nos ayudaría a imprimir solo los elementos del tipo Objeto. No será útil para imprimir elementos pertenecientes a otras clases como Double. Esto se debe a que Generic no admite la herencia de forma predeterminada y debe especificarse explícitamente con la palabra clave ''super''.// Would only print objects of type ''Object'' public static void printList(List<Object> list) { for (Object elem : list) System.out.println(elem + " "); System.out.println(); }
List<?>
: Esto podría ayudarnos a tener un método común para imprimir cualquier tipo de datos. Podríamos usar este método para imprimir instancias de cualquier tipo.// The type would really depend on what is being passed public static void printList(List<?> list) { for (Object elem: list) System.out.print(elem + " "); System.out.println(); }
En tiempo de ejecución, la JVM solo verá Set
debido al borrado de tipo.
En tiempo de compilación, hay una diferencia:
Set<Object>
parametrizado como tipo E
con Object
, Set.add(E element)
se parametrizará como Set.add(Object element)
.
Set<?>
otro lado, agrega un comodín en un tipo E
para que Set.add(E element)
se traduzca a Set.add(? element)
. Como esto no es compilable, Java lo "traduce" como Set.add(null element)
. Significa que no puede agregar nada a ese conjunto (excepto un nulo). La razón es que el comodín está haciendo referencia a un tipo desconocido.
Estaba explicando este elemento a mi amigo y, específicamente, solicité el método "safeAdd" como un contador para el ejemplo inseguroAdd. Asi que aqui esta.
public static void main(String[] args) {
List<String> strings = new ArrayList<String>();
unsafeAdd(strings, new Integer(42)); // No compile time exceptions
// New
safeAdd(strings, new Integer(42)); // Throwing an exception at compile time
String s = strings.get(0); // Compiler-generated cast
}
private static void unsafeAdd(List list, Object o) {
list.add(o);
}
private static <E> void safeAdd(List<E> list, E o) {
list.add(o);
}
La diferencia entre Set<Object>
y Set<?>
Es que una variable de tipo Set<?>
Puede tener un tipo genérico más específico asignado, como en:
Set<?> set = new HashSet<Integer>();
mientras que Set<Object>
solo se puede asignar Set<Object>
:
Set<Object> set = new HashSet<Integer>(); // won''t compile
El Set<Object>
sigue siendo útil, ya que cualquier objeto puede incluirse en él. Es muy parecido al Set
procesar en ese sentido, pero funciona mejor con el sistema de tipo genérico.
Set
: no genéricos aquí, inseguro. Agrega lo que quieras
Set<?>
: Un conjunto de cierto tipo que no conocemos de nuestro alcance. Lo mismo que Set<? extends Object>
Set<? extends Object>
. Puede hacer referencia a Conjuntos de cualquier tipo, pero ese tipo debe definirse en el punto donde el conjunto se instancia realmente. Con la referencia wildcarded, no podemos modificar el conjunto (no podemos agregar o eliminar nada que no sea nulo). Es como una vista.
Set<Object>
: Set containing Objects (solo clase base, no subclases). Quiero decir que puede instanciar el conjunto usando Colecciones de tipo Objeto, como HashSet<Object>
pero no con HashSet<String>
. Por supuesto, puede agregar elementos de cualquier tipo al conjunto, pero solo porque sucede que todo es un Objeto o una subclase de Objeto. Si el conjunto se definió como Conjunto, solo puede agregar Números y subclases de Número, y nada más.
Set<?> set = new HashSet<String>();
set.add(new Object()); // compile time error
Como no sabemos qué significa el tipo de conjunto de elementos, no podemos agregarle objetos. El método add()
toma argumentos de tipo E
, el tipo de elemento del Set
. Cuando el parámetro de tipo real es ?
, representa un tipo desconocido. Cualquier parámetro que pasemos para agregar debería ser un subtipo de este tipo desconocido. Como no sabemos qué tipo es, no podemos pasar nada. La única excepción es null
, que es miembro de todo tipo.
Dado un Set<?>
, Podemos llamar a get()
y hacer uso del resultado. El tipo de resultado es un tipo desconocido, pero siempre sabemos que es un objeto. Por lo tanto, es seguro asignar el resultado de get()
a una variable de tipo Object
o pasarlo como un parámetro donde se espera el tipo Object
.
- un tipo sin procesar (
Set
) trata el tipo como si no tuviera información de tipo genérico . Tenga en cuenta el efecto sutil que no solo ignorará el argumento de tipoT
, sino también todos los demás argumentos de tipo que puedan tener los métodos de ese tipo. Puede agregarle cualquier valor y siempre devolveráObject
. -
Set<Object>
es unSet
que acepta todos los objetosObject
(es decir, todos los objetos) y devolverá objetos de tipoObject
. -
Set<?>
Es unSet
que acepta todos los objetos de algún tipo específico pero desconocido y devolverá objetos de ese tipo. Como no se conoce nada acerca de este tipo, no se puede agregar nada a ese conjunto (exceptonull
) y lo único que se sabe sobre los valores que devuelve es que son algún subtipo deObject
.