JavaRush /Blog Java /Random-ES /¿Qué son los genéricos en Java?

¿Qué son los genéricos en Java?

Publicado en el grupo Random-ES
¡Hola! Hoy hablaremos de genéricos. ¡Debo decir que aprenderás muchas cosas nuevas! No sólo esto, sino que las próximas conferencias estarán dedicadas a los genéricos. ¿Qué son los genéricos en Java? - 1 Por lo tanto, si este tema te interesa, estás de suerte: hoy aprenderás mucho sobre las características de los genéricos. Pues si no, ¡cálmate y relájate! :) Este es un tema muy importante y necesitas saberlo. Comencemos con uno simple: "qué" y "por qué". ¿Qué son los genéricos? Los genéricos son tipos con un parámetro. Al crear un genérico, especifica no solo su tipo, sino también el tipo de datos con los que debería trabajar. Creo que ya se te ha ocurrido el ejemplo más obvio: ¡este es ArrayList! Así es como normalmente lo creamos en el programa:
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<String> myList1 = new ArrayList<>();
       myList1.add("Test String 1");
       myList1.add("Test String 2");
   }
}
Como puedes imaginar, la peculiaridad de la lista es que no será posible "meterlo" todo en ella: funciona exclusivamente con objetos String. Ahora hagamos una breve excursión a la historia de Java e intentemos responder la pregunta: "¿por qué?" Para ello, nosotros mismos escribiremos una versión simplificada de la clase ArrayList. Nuestra lista solo puede agregar datos a la matriz interna y recibir estos datos:
public class MyListClass {

   private Object[] data;
   private int count;

   public MyListClass() {
       this.data = new Object[10];
       this.count = 0;
   }

   public void add(Object o) {
       this.data[count] = o;
       count++;
   }

   public Object[] getData() {
       return data;
   }
}
Digamos que queremos que nuestra lista almacene solo números Integer. No tenemos genéricos. No podemos especificar explícitamente la instancia o de check Integeren el archivo add(). Entonces toda nuestra clase será adecuada solo para Integer, ¡y tendremos que escribir la misma clase para todos los tipos de datos existentes en el mundo! Decidimos confiar en nuestros programadores y simplemente dejar un comentario en el código para que no agreguen nada innecesario allí:
//use it ONLY with Integer data type
public void add(Object o) {
   this.data[count] = o;
   count++;
}
Uno de los programadores pasó por alto este comentario y, sin darse cuenta, intentó poner números mezclados con cadenas en la lista y luego calcular su suma:
public class Main {

   public static void main(String[] args) {

       MyListClass list = new MyListClass();
       list.add(100);
       list.add(200);
       list.add("Lolkek");
       list.add("Shalala");

       Integer sum1 = (Integer) list.getData()[0] + (Integer) list.getData()[1];
       System.out.println(sum1);

       Integer sum2 = (Integer) list.getData()[2] + (Integer) list.getData()[3];
       System.out.println(sum2);
   }
}
Salida de la consola: 300 Excepción en el hilo "principal" java.lang.ClassCastException: java.lang.String no se puede convertir a java.lang.Integer en Main.main(Main.java:14) ¿ Qué es lo peor en esta situación? Lejos de ser la falta de atención de un programador. Lo peor es que el código incorrecto terminó en un lugar importante de nuestro programa y se compiló exitosamente . Ahora veremos el error no en la etapa de codificación, sino solo en la etapa de prueba (¡y esto es en el mejor de los casos!). Corregir errores más adelante en el desarrollo cuesta mucho más, tanto dinero como tiempo. Ésta es precisamente la ventaja de los genéricos: una clase genérica permitirá a un programador desafortunado detectar un error inmediatamente. ¡El código simplemente no se compila!
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Integer> myList1 = new ArrayList<>();

       myList1.add(100);
       myList1.add(100);
       myList1.add("Lolkek");//¡error!
       myList1.add("Shalala");//¡error!
   }
}
El programador inmediatamente "recobrará el sentido" y se corregirá instantáneamente. Por cierto, no tuvimos que crear nuestra propia clase Listpara ver este tipo de error. ¡Simplemente elimine los corchetes de tipo ( <Integer>) de una ArrayList normal!
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

      List list = new ArrayList();

      list.add(100);
      list.add(200);
      list.add("Lolkek");
      list.add("Shalala");

       System.out.println((Integer) list.get(0) + (Integer) list.get(1));
       System.out.println((Integer) list.get(2) + (Integer) list.get(3));
   }
}
Salida de la consola: 300 Excepción en el hilo "main" java.lang.ClassCastException: java.lang.String no se puede convertir a java.lang.Integer en Main.main(Main.java:16) Es decir, incluso usando herramientas "nativas" Java, puedes cometer este error y crear una colección insegura. Sin embargo, si pegamos este código en IDEa, vemos una advertencia: " Llamada no marcada para agregar (E) como miembro del tipo sin formato de java.util.List " Nos indica que algo podría salir mal al agregar un elemento a un La colección sin genéricos no es así. Pero, ¿qué significa la frase “tipo crudo”? La traducción literal será bastante precisa: " tipo crudo " o " tipo sucio ". Raw typees una clase genérica de la que se ha eliminado su tipo. En otras palabras, List myList1esto es Raw type. Lo contrario raw typees generic typeuna clase genérica (también conocida como clase parameterized type), creada correctamente, con una especificación de tipo. Por ejemplo, List<String> myList1. Quizás tengas una pregunta: ¿por qué está permitido su uso raw types? La razón es sencilla. Los creadores de Java dejaron soporte en el lenguaje raw typespara no crear problemas de compatibilidad. Cuando se lanzó Java 5.0 (los genéricos aparecieron por primera vez en esta versión), ya se había escrito mucho código usando raw types. Por tanto, esta posibilidad todavía existe hoy. Ya hemos mencionado más de una vez en nuestras conferencias el libro clásico de Joshua Bloch “Effective Java”. Como uno de los creadores del idioma, no ignoró el tema del uso raw typesy en el libro generic types. ¿Qué son los genéricos en Java? - 2El capítulo 23 de este libro tiene un título muy elocuente: “No utilice tipos sin formato en código nuevo”. Esto es algo que debe recordar. Cuando utilice clases genéricas, nunca las convierta generic typeen raw type.

Métodos escritos

Java le permite escribir métodos individuales, creando los llamados métodos genéricos. ¿Por qué son convenientes estos métodos? En primer lugar, porque te permiten trabajar con diferentes tipos de parámetros. Si la misma lógica se puede aplicar de forma segura a diferentes tipos, un método genérico es una gran solución. Veamos un ejemplo. Digamos que tenemos algún tipo de lista myList1. Queremos eliminar todos los valores y llenar todos los espacios libres con un nuevo valor. Así se verá nuestra clase con un método genérico:
public class TestClass {

   public static <T> void fill(List<T> list, T val) {
       for (int i = 0; i < list.size(); i++)
           list.set(i, val);
   }

   public static void main(String[] args) {

       List<String> strings = new ArrayList<>();
       strings.add("Старая строка 1");
       strings.add("Старая строка 2");
       strings.add("Старая строка 3");

       fill(strings, "Новая строка");

       System.out.println(strings);

       List<Integer> numbers = new ArrayList<>();
       numbers.add(1);
       numbers.add(2);
       numbers.add(3);

       fill(numbers, 888);
       System.out.println(numbers);
   }
}
Preste atención a la sintaxis, parece un poco inusual:
public static <T> void fill(List<T> list, T val)
El tipo de retorno está precedido por <T>, que indica un método genérico. En este caso, el método toma 2 parámetros como entrada: una lista de objetos T y otro objeto T separado. Al usar <T>, se logra la tipificación del método: no podemos pasar una lista de cadenas y un número allí. Una lista de cadenas y una cadena, una lista de números y un número, una lista de nuestros objetos Caty otro objeto Cat: esa es la única manera. El método main()demuestra claramente que fill()funciona fácilmente con diferentes tipos de datos. Primero, toma como entrada una lista de cadenas y una cadena, y luego una lista de números y un número. Salida de la consola: [Nueva línea, Nueva línea, Nueva línea] [888, 888, 888] Imagínese si fill()necesitáramos lógica de método para 30 clases diferentes y no tuviéramos métodos genéricos. ¡Nos veríamos obligados a escribir el mismo método 30 veces, solo para diferentes tipos de datos! ¡Pero gracias a los métodos genéricos, podemos reutilizar nuestro código! :)

clases mecanografiadas

¡No sólo puedes usar las clases genéricas proporcionadas en Java, sino también crear las tuyas propias! He aquí un ejemplo sencillo:
public class Box<T> {

   private T t;

   public void set(T t) {
       this.t = t;
   }

   public T get() {
       return t;
   }

   public static void main(String[] args) {

       Box<String> stringBox = new Box<>();

       stringBox.set("Старая строка");
       System.out.println(stringBox.get());
       stringBox.set("Новая строка");

       System.out.println(stringBox.get());

       stringBox.set(12345);//ошибка компиляции!
   }
}
Nuestra clase Box<T>(“cuadro”) está escrita. Al asignarle un tipo de datos ( ) durante la creación <T>, ya no podremos colocar en él objetos de otros tipos. Esto se puede ver en el ejemplo. Al crear, especificamos que nuestro objeto funcionará con cadenas:
Box<String> stringBox = new Box<>();
Y cuando en la última línea de código intentamos poner el número 12345 dentro del cuadro, ¡obtenemos un error de compilación! ¡Así de simple, creamos nuestra propia clase genérica! :) Esto concluye nuestra conferencia de hoy. ¡Pero no nos despedimos de los genéricos! En las próximas conferencias hablaremos de funciones más avanzadas, ¡así que no te despidas! ) ¡Buena suerte en tus estudios! :)
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION