"¡Hola, amigo!"

"¡Hola, Ellie!"

"Hoy, Rishi y yo les contaremos todo sobre los genéricos".

"Espera, creo que ya sé casi todo".

"Casi todo, pero no todo".

"¿En serio? Está bien, estoy listo para escuchar".

"Entonces comencemos".

"En Java, los genéricos son clases que tienen parámetros de tipo".

"En cuanto a por qué se inventaron los genéricos, consulte los comentarios en el código:"

Ejemplo
ArrayList stringList = new ArrayList();
stringList.add("abc"); // Add a string to the list
stringList.add("abc"); // Add a string to the list
stringList.add( 1 ); // Add a number to the list

for(Object o: stringList)
{
 String s = (String) o; // There will be an exception here when we get to the integer
}

Cómo resolver el problema usando Genéricos:

Ejemplo
ArrayList<String> stringList = new ArrayList<String>();
stringList.add("abc"); // Add a string to the list
stringList.add("abc"); // Add a string to the list
stringList.add( 1 ); // There will be a compilation error here

for(Object o: stringList)
{
 String s = (String) o;
}

"Este código simplemente no se compilará, y el error causado por agregar el tipo de datos incorrecto se notará durante la compilación".

"Sí, ya sé esto".

"Está bien, bien. La repetición es la madre del aprendizaje".

"Pero los creadores de Java fueron un poco perezosos cuando crearon los genéricos. En lugar de hacer tipos completos con parámetros, deslizaron una optimización hábil. En realidad, no agregaron ninguna información sobre los parámetros de tipo a los genéricos. En cambio, todos los la magia ocurre durante la compilación".

Código con genéricos
List<String> strings = new ArrayList<String>();
strings.add("abc");
strings.add("abc");
strings.add( 1); // Compilation error

for(String s: strings)
{
 System.out.println(s);
}
lo que realmente sucede
List strings = new ArrayList();

strings.add((String)"abc");
strings.add((String)"abc");
strings.add((String) 1); // Compilation error

for(String s: strings)
{
 System.out.println(s);
}

"Eso es resbaladizo".

"Sí, pero este enfoque tiene un efecto secundario.  No se almacena información sobre los parámetros de tipo dentro de una clase genérica.  Este enfoque más tarde se conoció como borrado de tipo ".

"En otras palabras, si tiene su propia clase con parámetros de tipo, no puede usar información sobre ellos dentro de la clase".

Código con genéricos
class Zoo<T>
{
 ArrayList<T> pets = new ArrayList<T>();

 public T createAnimal()
 {
  T animal = new T();
  pets.add(animal)
  return animal;
 }
}
lo que realmente sucede
class Zoo
{
 ArrayList pets = new ArrayList();

 public Object createAnimal()
 {
  Object animal = new ???();
  pets.add(animal)
  return animal;
 }
}

"Durante la compilación, todos los tipos de parámetros se reemplazan con Object. Y dentro de la clase no hay información sobre el tipo que se le pasa".

"Sí, estoy de acuerdo, eso no es lo mejor".

"No es tan aterrador. Te diré más tarde cómo solucionar este problema".

Pero hay más Java le permite especificar un tipo principal para los parámetros de tipo. La palabra clave extends se utiliza para esto. Por ejemplo:

Código con genéricos
class Zoo<T extends Cat>
{
 T cat;

 T getCat()
 {
  return cat;
 }

 void setCat (T cat)
 {
  this.cat = cat;
 }

 String getCatName()
 {
  return this.cat.getName();
 }
}
lo que realmente sucede
class Zoo
{
 Cat cat;

 Cat getCat()
 {
  return cat;
 }

 void setCat(Cat cat)
 {
  this.cat = cat;
 }

 String getCatName()
 {
  return this.cat.getName();
 }
}

"Observe dos hechos:"

"Primero, no puede pasar cualquier tipo como parámetro; solo puede pasar un Cat o una clase que herede Cat".

"En segundo lugar, dentro de la clase Zoo, las variables de tipo T ahora pueden llamar a los métodos de la clase Cat.  La columna de la derecha explica por qué (porque Cat se sustituirá en todos los lugares donde haya una T)".

"Sí. Si decimos que Cat o una subclase de Cat se pasa como argumento de tipo, entonces estamos seguros de que el tipo T siempre tendrá los métodos de la clase Cat".

"Bueno, eso es inteligente".