1. Información general
En este tutorial rápido, veremos las similitudes y diferencias entre y en Java Generics .
Sin embargo, al ser un tema avanzado, es imperativo obtener una comprensión básica del tema antes de sumergirnos en el meollo del asunto.
2. Antecedentes de los genéricos
Los genéricos se introdujeron en JDK 5 para eliminar errores en tiempo de compilación y fortalecer la seguridad de tipos. Esta seguridad de tipo adicional elimina la conversión en algunos casos de uso y permite a los programadores escribir algoritmos genéricos, los cuales pueden conducir a un código más legible.
Por ejemplo, antes de JDK 5, tendríamos que trabajar con los elementos de una lista usando casting. Esto, a su vez, creó una cierta clase de errores de tiempo de ejecución:
List aList = new ArrayList(); aList.add(new Integer(1)); aList.add("a_string"); for (int i = 0; i < aList.size(); i++) { Integer x = (Integer) aList.get(i); }
Ahora, este código tiene dos problemas que nos gustaría abordar:
- Necesitamos una conversión explícita para extraer valores de una lista ; el tipo depende del tipo de variable de la izquierda; en este caso, entero
- Obtendremos un error de tiempo de ejecución en la segunda iteración cuando intentemos convertir una_string en un entero.
Los genéricos cumplen el papel para nosotros:
List iList = new ArrayList(); iList.add(1); iList.add("a_string"); // compile time error for (int i = 0; i < iList.size(); i++) { int x = iList.get(i); }
El compilador nos dirá que no es posible agregar a_string a una lista de tipo Integer , lo cual es mejor que averiguarlo en tiempo de ejecución.
Además, no se necesita conversión explícita ya que el compilador ya sabe que iList contiene Integer s. Además, debido a la magia del unboxing, ni siquiera necesitamos un tipo Integer , su forma primitiva es suficiente.
3. Comodines en genéricos
Un signo de interrogación, o comodín, se usa en genéricos para representar un tipo desconocido. Puede tener tres formas:
- Comodines ilimitados : la lista representa una lista de tipo desconocido
- Comodines delimitados superiores : Lista representa una lista de Número o sus subtipos, como Integer y Double
- Comodines delimitados inferiores : la lista representa una lista de números enteros o sus supertipos Número y objeto
Ahora, dado que Object es el supertipo inherente de todos los tipos en Java, estaríamos tentados a pensar que también puede representar un tipo desconocido. En otras palabras, List y List podrían tener el mismo propósito. Pero no lo hacen.
Consideremos estos dos métodos:
public static void printListObject(List list) { for (Object element : list) { System.out.print(element + " "); } } public static void printListWildCard(List list) { for (Object element: list) { System.out.print(element + " "); } }
Dada una lista de enteros , diga:
List li = Arrays.asList(1, 2, 3);
printListObject (li) no se compilará y obtendremos este error:
The method printListObject(List) is not applicable for the arguments (List)
Mientras que printListWildCard (li) compilará y enviará 1 2 3 a la consola.
4. y - Las similitudes
En el ejemplo anterior, si cambiamos la firma del método para printListWildCard a:
public static void printListWildCard(List list)
Funcionaría de la misma manera que lo hizo printListWildCard (lista de lista) . Esto se debe al hecho de que Object es un supertipo de todos los objetos Java, y básicamente todo se extiende a Object . Por lo tanto, también se procesa una Lista de enteros .
En resumen, ¿ significa eso ? y ? Extended Object son sinónimos en este ejemplo .
Si bien en la mayoría de los casos eso sería cierto, también hay algunas diferencias . Veámoslos en la siguiente sección.
5. y - la diferencia
Los tipos confiables son aquellos cuyo tipo no se borra en tiempo de compilación. En otras palabras, la representación en tiempo de ejecución de un tipo no confiable tendrá menos información que su contraparte en tiempo de compilación, porque parte de ella se borrará.
Por regla general, los tipos parametrizados no son confiables. Esto significa que List y Map no son confiables. El compilador borra su tipo y los trata como una Lista y un Mapa respectivamente.
La única excepción a esta regla son los tipos de comodines ilimitados. Esto significa que Lista y Mapa son confiables .
Por otro lado, List no es confiable . Aunque sutil, esta es una diferencia notable.
Los tipos no confiables no se pueden usar en ciertas situaciones, como en un operador de instancia o como elementos de una matriz.
Entonces, si escribimos:
List someList = new ArrayList(); boolean instanceTest = someList instanceof List
Este código se compila y instanceTest es verdadero .
Pero, si usamos el operador instanceof en List :
List anotherList = new ArrayList(); boolean instanceTest = anotherList instanceof List;
entonces la línea 2 no se compila.
De manera similar, en el siguiente fragmento, la línea 1 se compila, pero la línea 2 no:
List[] arrayOfList = new List[1]; List[] arrayOfAnotherList = new List[1]
6. Conclusión
En este breve tutorial, vimos las similitudes y diferencias en y .
Si bien en su mayoría son similares, existen diferencias sutiles entre los dos en términos de que sean confiables o no.