JavaRush /Blog Java /Random-ES /Usar varargs cuando se trabaja con genéricos

Usar varargs cuando se trabaja con genéricos

Publicado en el grupo Random-ES
¡Hola! En la lección de hoy continuaremos estudiando los genéricos. Da la casualidad de que este es un tema importante, pero no hay ningún lugar adonde ir; esta es una parte extremadamente importante del lenguaje :) Cuando estudie la documentación de Oracle sobre genéricos o lea guías en Internet, encontrará los términos Tipos No Reifiables y Tipos Reifiables . ¿Qué tipo de palabra es “Reificable”? Incluso si todo va bien con el inglés, es poco probable que lo hayas conocido. ¡Intentemos traducir! Usando varargs cuando se trabaja con genéricos - 2
*gracias Google, me ayudaste mucho -_-*
Un tipo reifiable es un tipo cuya información está completamente disponible en tiempo de ejecución. En el lenguaje Java, estos incluyen tipos primitivos, tipos sin formato y tipos no genéricos. Por el contrario, los tipos no certificables son tipos cuya información se borra y deja de estar disponible en tiempo de ejecución. Estos son solo genéricos: List<String> , List<Integer> , etc.

Por cierto, ¿recuerdas qué son los varargs ?

En caso de que lo hayas olvidado, estos son argumentos de longitud variable. Resultan útiles en situaciones en las que no sabemos exactamente cuántos argumentos se pueden pasar a nuestro método. Por ejemplo, si tenemos una clase de calculadora y tiene un método sum. sum()Puedes pasar 2 números, 3, 5 o tantos como quieras al método . Sería muy extraño sobrecargar el método cada vez sum()para tener en cuenta todas las opciones posibles. En lugar de eso podemos hacer esto:
public class SimpleCalculator {

   public static int sum(int...numbers) {

       int result = 0;

       for(int i : numbers) {

           result += i;
       }

       return result;
   }

   public static void main(String[] args) {

       System.out.println(sum(1,2,3,4,5));
       System.out.println(sum(2,9));
   }
}
Salida de consola:

15
11
Por lo tanto, cuando se utiliza varargsen combinación con genéricos, existen algunas características importantes. Veamos este código:
import javafx.util.Pair;
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static <E> void addAll(List<E> list, E... array) {

       for (E element : array) {
           list.add(element);
       }
   }

   public static void main(String[] args) {
       addAll(new ArrayList<String>(),  //  здесь все нормально
               "Leonardo da Vinci",
               "Vasco de Gama"
       );

       // а здесь мы получаем предупреждение
       addAll(new ArrayList<Pair<String, String>>(),
               new Pair<String, String>("Leonardo", "da Vinci"),
               new Pair<String, String>("Vasco", "de Gama")
       );
   }
}
El método toma una lista y cualquier número de objetos addAll()como entrada y luego agrega todos estos objetos a la lista. En el método llamamos a nuestro método dos veces . La primera vez agregamos dos líneas regulares. Todo está bien aquí. La segunda vez agregamos dos objetos . Y aquí de repente recibimos una advertencia: List<E>Emain()addAll()ListListPair<String, String>

Unchecked generics array creation for varargs parameter
¿Qué significa? ¿Por qué recibimos una advertencia y qué tiene que ver con ella array? Array- ¡Esta es una matriz y no hay matrices en nuestro código! Empecemos por el segundo. La advertencia menciona una matriz porque el compilador convierte argumentos de longitud variable (varargs) en una matriz. En otras palabras, la firma de nuestro método es addAll():
public static <E> void addAll(List<E> list, E... array)
En realidad se parece a esto:
public static <E> void addAll(List<E> list, E[] array)
Es decir, en el método main(), el compilador convertirá nuestro código en esto:
public static void main(String[] args) {
   addAll(new ArrayList<String>(),
      new String[] {
        "Leonardo da Vinci",
        "Vasco de Gama"
      }
   );
   addAll(new ArrayList<Pair<String,String>>(),
        new Pair<String,String>[] {
            new Pair<String,String>("Leonardo","da Vinci"),
            new Pair<String,String>("Vasco","de Gama")
        }
   );
}
Todo está bien con la matriz String. Pero con una matriz Pair<String, String>, no. El hecho es que Pair<String, String>este es un tipo no acreditable. Durante la compilación, se borrará toda la información sobre los tipos de parámetros (<String, String>). En Java no se permite la creación de matrices a partir de tipos no verificables . Puede verificar esto si intenta crear manualmente una matriz Pair<String, String>
public static void main(String[] args) {

   //  ошибка компиляции! Generic array creation
  Pair<String, String>[] array = new Pair<String, String>[10];
}
La razón es obvia: seguridad de tipos. Como recuerdas, al crear una matriz, debes indicar qué objetos (o primitivos) almacenará esta matriz.
int array[] = new int[10];
En una de las lecciones anteriores, examinamos en detalle el mecanismo de borrado de tipos. Entonces, en este caso, como resultado de borrar tipos, perdimos la información de que Pairlos pares estaban almacenados en nuestros objetos <String, String>. Crear una matriz no será seguro. Cuando utilice métodos con varargsy genéricos, asegúrese de recordar el borrado de tipos y cómo funciona exactamente. Si tiene absoluta confianza en el código que ha escrito y sabe que no causará ningún problema, puede desactivar las varargsadvertencias asociadas mediante una anotación.@SafeVarargs
@SafeVarargs
public static <E> void addAll(List<E> list, E... array) {

   for (E element : array) {
       list.add(element);
   }
}
Si agrega esta anotación a su método, la advertencia que encontramos anteriormente no aparecerá. Otro posible problema al utilizar varargsgenéricos juntos es la contaminación acumulada. Usando varargs cuando se trabaja con genéricos - 4La contaminación puede ocurrir en las siguientes situaciones:
import java.util.ArrayList;
import java.util.List;

public class Main {

   static List<String> makeHeapPollution() {
       List numbers = new ArrayList<Number>();
       numbers.add(1);
       List<String> strings = numbers;
       strings.add("");
       return strings;
   }

   public static void main(String[] args) {

       List<String> stringsWithHeapPollution = makeHeapPollution();

       System.out.println(stringsWithHeapPollution.get(0));
   }
}
Salida de consola:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
En términos simples, la contaminación del montón es una situación en la que los objetos de tipo 1 deberían estar en el montón А, pero los objetos de tipo terminan allí Bdebido a errores de seguridad de tipo. En nuestro ejemplo esto es lo que sucede. Primero creamos una variable Raw numbersy le asignamos una colección genérica ArrayList<Number>. Después de eso agregamos el número allí 1.
List<String> strings = numbers;
En esta línea, el compilador intentó advertirnos sobre posibles errores emitiendo la advertencia “ Asignación no marcada… ”, pero lo ignoramos. Como resultado, tenemos una variable genérica de tipo List<String>, que apunta a una colección genérica de tipo ArrayList<Number>. ¡Esta situación claramente puede causar problemas! Esto es lo que pasa. Usando nuestra nueva variable, agregamos una cadena a la colección. El montón estaba contaminado: primero agregamos un número y luego una cadena a la colección escrita. El compilador nos advirtió, pero ignoramos su advertencia y recibimos resultados ClassCastExceptionsolo mientras el programa se estaba ejecutando. ¿Qué tiene que ver con eso varargs? El uso varargscon genéricos puede provocar fácilmente una gran contaminación. He aquí un ejemplo sencillo:
import java.util.Arrays;
import java.util.List;

public class Main {

   static void makeHeapPollution(List<String>... stringsLists) {
       Object[] array = stringsLists;
       List<Integer> numbersList = Arrays.asList(66,22,44,12);

       array[0] = numbersList;
       String str = stringsLists[0].get(0);
   }

   public static void main(String[] args) {

       List<String> cars1 = Arrays.asList("Ford", "Fiat", "Kia");
       List<String> cars2 = Arrays.asList("Ferrari", "Bugatti", "Zaporozhets");

       makeHeapPollution(cars1, cars2);
   }
}
¿Que está pasando aqui? Debido al borrado de tipos, nuestras hojas de parámetros (las llamaremos "hojas" en lugar de "listas" por conveniencia) son:
List<String>...stringsLists
- se convertirá en una matriz de hojas - List[]de tipo desconocido (no olvide que varargs se convierte en una matriz normal como resultado de la compilación). Debido a esto, podemos realizar fácilmente una asignación a una variable Object[] arrayen la primera línea del método: ¡los tipos se han borrado de nuestras hojas! Y ahora tenemos una variable de tipo Object[], donde podemos agregar cualquier cosa: ¡todos los objetos en Java se heredan Object! En este momento sólo tenemos una serie de hojas de cuerdas. Pero gracias al uso varargsy borrado de tipos, podemos agregarles fácilmente una hoja de números, que es lo que hacemos. Como resultado, contaminamos el montón al mezclar objetos de diferentes tipos. El resultado será la misma excepción ClassCastExceptional intentar leer una cadena de la matriz. Salida de consola:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Estas son las consecuencias inesperadas que pueden resultar del uso de un mecanismo aparentemente simple varargs:) Y aquí es donde nuestra conferencia de hoy llega a su fin. No olvide resolver un par de problemas y, si le queda tiempo y energía, estudie literatura adicional. ¡ “ Java efectivo ” no se leerá solo! :) ¡Nos vemos!
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION