"¡Hola! Voy a continuar con la lección de Ellie sobre genéricos. ¿Listo para escuchar?"

"Sí."

"Entonces comencemos".

"Lo primero que debe saber es que los métodos de una clase también pueden tener sus propios parámetros de tipo".

"Lo sé."

"No, me refiero específicamente a sus propios parámetros de tipo: "

Ejemplo
class Calculator
{
  T add(T a, T b); // Add
  T sub(T a, T b); // Subtract
  T mul(T a, T b); // Multiply
  T div(T a, T b); // Divide
}

"Estos parámetros de tipo pertenecen específicamente a los métodos. La clase no tiene parámetros. Incluso podría declarar estos métodos estáticos y llamarlos sin un objeto".

"Ya veo. ¿El punto de los parámetros de tipo en los métodos es el mismo que para las clases?"

"Sí. Pero hay algo nuevo".

"Como ya sabes, puedes usar un comodín en la declaración de tipo. Entonces imagina la siguiente situación:"

Ejemplo 1
public void doSomething(List<? extends MyClass> list)
{
 for(MyClass object : list)
 {
  System.out.println(object.getState()); // Everything works well here.
 }
}

"Pero qué pasa si queremos agregar un nuevo artículo a la colección:"

Ejemplo 2
public void doSomething(List<? extends MyClass> list)
{
 list.add(new MyClass()); // Error!
}

"El problema es que, en general, al método doSomething se le puede pasar una lista cuyos elementos no son objetos de MyClass, sino objetos de cualquier subclase de MyClass. ¡Pero no puede agregar objetos de MyClass a esa lista!"

"Ah. Entonces, ¿qué se puede hacer al respecto?"

"Nada. En esta situación, no puedes hacer nada. Pero esto les dio a los creadores de Java algo en lo que pensar. Y se les ocurrió una nueva palabra clave: super ".

"La sintaxis se ve casi igual:"

List<? super MyClass> list

Pero hay una distinción importante entre extends y super.

"«? extends T» significa que la clase debe ser descendiente de T".

"«? super T» significa que la clase debe ser un ancestro de T".

"Holy moly. Entonces, ¿dónde se usa esto?"

"«? super T» se usa cuando un método implica agregar a una colección de objetos T. En este caso, podría ser una colección de objetos T o cualquier ancestro de T".

"Ah. El objeto AT se puede asignar a una variable de referencia cuyo tipo es cualquiera de los ancestros de T"

"Honestamente, este enfoque no se usa muy a menudo. Además, tiene una deficiencia. Por ejemplo:"

Ejemplos
public void doSomething(List<? super MyClass> list)
{
 for(MyClass object : list) // Error!
 {
  System.out.println(object.getState());
 }
}
public void doSomething(List<? super MyClass> list)
{
 list.add(new MyClass()); // Everything works well here.
}

"Ahora el primer ejemplo no funciona".

"Dado que list podría incluso ser List<Object> (Object es la superclase superior de MyClass), esencialmente estamos escribiendo el siguiente código no válido:"

Ejemplo 1
List<Object> list;

for(MyClass object : list) // Error!
{
 System.out.println(object.getState());
}

"Ya veo. Gracias por la interesante lección".

"De nada."