"Oi! Vou continuar a aula de Ellie sobre genéricos. Pronto para ouvir?"

"Sim."

"Então vamos começar."

"A primeira coisa que você precisa saber é que os métodos de uma classe também podem ter seus próprios parâmetros de tipo."

"Eu sei."

"Não, quero dizer especificamente seus próprios parâmetros de tipo: "

Exemplo
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
}

"Esses parâmetros de tipo pertencem especificamente aos métodos. A classe não tem parâmetros. Você pode até mesmo declarar esses métodos estáticos e chamá-los sem um objeto."

"Entendo. O objetivo dos parâmetros de tipo nos métodos é o mesmo das classes?"

"Sim. Mas há algo novo."

"Como você já sabe, você pode usar um curinga na declaração de tipo. Então imagine a seguinte situação:"

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

"Mas e se quisermos adicionar um novo item à coleção:"

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

"O problema é que, em geral, o método doSomething pode receber uma lista cujos elementos não são objetos MyClass, mas sim objetos de qualquer subclasse de MyClass. Mas você não pode adicionar objetos MyClass a essa lista!"

"Ah. Então, o que pode ser feito sobre isso?"

"Nada. Nesta situação, você não pode fazer nada. Mas isso deu aos criadores de Java algo em que pensar. E eles criaram uma nova palavra-chave: super ."

"A sintaxe parece quase a mesma:"

List<? super MyClass> list

Mas há uma distinção importante entre extends e super.

"«? estende T» significa que a classe deve ser descendente de T."

"«? super T» significa que a classe deve ser um ancestral de T."

"Caramba. Então, onde isso é usado?"

"«? super T» é usado quando um método envolve adicionar a uma coleção de T objetos. Nesse caso, pode ser uma coleção de T objetos ou qualquer ancestral de T."

"Ah. O objeto AT pode ser atribuído a uma variável de referência cujo tipo é qualquer um dos ancestrais de T"

"Honestamente, essa abordagem não é usada com muita frequência. Além do mais, ela tem uma falha. Por exemplo:"

Exemplos
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.
}

"Agora o primeiro exemplo não funciona."

"Como a lista pode até ser uma List<Object> (Object é a superclasse mais importante de MyClass), estamos essencialmente escrevendo o seguinte código inválido:"

Exemplo 1
List<Object> list; 

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

"Entendo. Obrigado pela lição interessante."

"De nada."