Por que usar &lt ? extends [CLASS] &gt

Galera, minha dúvida é bem direta: qual a real utilidade de usar &lt? extends [CLASS]&gt??
::: onde [CLASS] é o nome de uma classe ou interface

Entendo que: usando uma declaração genérica, ao ser instanciada, uma classe passa a ter um tipo fixo para seus parâmetros (no caso das coleções).
Quando eu instancio uma nova coleção, eu defino através do &lt type &gt qual o tipo de dados que será armazenado na coleção. Digamos que eu instancie uma List com List list = new List&lt Collection &gt()
Nela estamos definindo que poderemos inserir QUALQUER objeto que seja uma Collection. Isso permite, é claro, que qualquer classe que implemente direta ou indiretamente a interface Collection poderá ser adicionada à "list".
Isso pq, uma vez que uma classe própria do usuário estenda Collection, ela passa a ter um relacionamento "é um" com esta.

Agora vejamos na prática:

  • classe genérica Journal (a usarei para meus exemplos) -
public class Journal&lt T &gt
  {
   private T core;

   public Journal&lt T &gt(T obj)
     { set(obj); }

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

Agora uma classe que estenderá Journal

public class JournalModel
  {
   private String core;

   public JournalModel(String core)
     { this.core = core; }
  }

E a classe que será um JournalModel:

public class MeuJournal extends JournalModel
  {
   public MeuJournal(String txt)
     { super(txt); }
  }

Agora uma classe que usa a classe Journal:

public class JournalTest
  {
   public static void main(String[] args)
     {
      JournalModel jm = new JournalModel("teste journalmodel");

      Journal&lt JournalModel &gt j = new Journal&lt JournalModel &gt(jm);

      MeuJournal mj = new MeuJournal("teste meujournal");

      j.set(mj);
     }
  }

Notemos que: MeuJournal "é um" JournalModel.

Percebamos que no código: primeiro instancio um JournalModel.
Depois eu instancio um Journal declarando como seu tipo (já que é uma classe genérica) como JournalModel, e passo como argumento ao construtor o meu objeto JournalModel instanciado anteriormente.
Ou seja, se o tipo da minha variável Journal é JournalModel, então um objeto JournalModel tem de ser adicionado sem problemas!
Em seguida instanciei um objeto MeuJournal. Lembre-se que ele estende JournalModel e, portanto, "é um" JournalModel. Então eu o passo para o objeto Journal, pq MeuJournal "é um " JournalModel, correto?

Então agora me diz, por que eu usaria &lt ? extends JournalModel &gt, se, bastando minha classe estender JournalModel, ela já "é um" JournalModel?

Qual a diferença entre eu declarar assim:

j = new Journal&lt JournalModel &gt();

e

j = new Journal&lt ? extends JournalModel &gt();

???


Aproveitando o gancho, qual a utilidade de se usar: &lt ? super JournalModel &gt ???
Alguém poderia dar um exemplo de "quando" isso seria útil?

Desde já agradecido pelos potenciais esclarecimentos.

Olá,

Tem esse post no blog do nosso Ilmo. louds que fala sobre os wildcards:

http://www.kumpera.net/blog/index.php/2006/08/08/a-parte-bizarra-de-generics/

Pelo menos para mim ajudou a entender melhor essa funcionalidade.

[]s,
Sami

Para entender isso você tem que passar a olhar da perspectiva de quem constrói a classe, não de quem usa.

Você vai construir uma lista inteiramente nova, mas que serve apenas para InetAddresses. Como dizer isso na declaração de usa classe?

class ListaDeInteiro<E extends InetAddress>

Da mesma forma, vamos supor que você queira criar um método novo para trabalhar com qualquer classe que fosse uma lista de InetAddress, ou filhos dele.

public static void operacaoNaLista(List&lt? extends InetAddress&gt lista);

Para mais detalhes, dê uma olhada no link que o colega passou. :slight_smile:

<? extends class > qdo vc usa isso, pelo q entendi da kathy vc ta dizendo que pode criar objetos daquela class ou dos seus subtipos... ou seja as class que estiver abaixo dela.. por exemplo : dog extends animal.. se vc usar a class animal vc pode criar objeto de animal.. o uso disso com generics é que nao é permitido isso com generics: [code] List my = ArrayList(); [/code] isso nao é permitido.. em tempo de compilação.. entao usa a sintaxe do coringa <? extends..> bom eu entendi assim segundo o cap 7 da kathy.. se eu tiver errado alguem me corrigi eheh!

Não é só usar:

public final class MyCollection< InetAdress >

A declaração de classe acima não estipula que só poderão ser criadas coleções de InetAdresses?

public static void operacaoNaLista(List< InetAddress > lista);

Faz a mesma coisa não é? Como o parâmetro aceita uma List de InetAdress, qualquer objeto que estenda InetAdress poderá ser passado como parâmetro.

É o que estou tentando dizer. Se eu criar isso:

List< Animal > list = new List< Animal >();

Poderei então adicionar, nessa lista, qualquer classe (Dog, Cat, Frog) que estenda Animal.

Então pq simplesmente não usar o < Type >?


Bom, eu já havia digitado tudo isso aí em cima, porém, antes de dar submit, abri o link que o usuário Sami Koivu passou e, me surpreendi! Até que enfime vi alguma lógica no extends e super dos Generics.

Obrigado à ajuda de todos!

Aliás, que grata surpresa é o blog do Louds!!!
Íncrível eu não conhecer… :oops:

Não fazem a mesma coisa.

Suponha:
MyCollection<INetAddress>
MyCollection2<E extends INetAddress>

O usuário da segunda lista pode fazer:
MyCollection2<INetSocketAddress> lst = new MyCollection2<INetSocketAddress>();

Agora, ele não pode atribuir mais qualquer INetAddress para lst. Somente os que forem filhos de InetSocketAddress. Note que com a segunda declaração, você deu ao usuário a opção de escolher o tipo de dados da lista, desde que ele seja filho do tipo que você especificou.
Por isso, agora ele pode fazer, sem cast:
INetSocketAddress x = lst.get(0);

Tente fazer isso para a primeira lista. Impossível. Ela força que o usuário use o tipo INetAddress. Portanto, se ele quiser usar INetSocketAddress nessa lista terá que fazer:
INetSocketAddress x = (INetSocketAddress) lst.get(0);

Da mesma foram o método de cópia.
Se você tem
copiaDe(List&lt? extends INetAddress&gt)

Você poderá receber a lista lst como parâmetro. Ela é uma lista de um tipo filho de INetAddress, não do tipo InetAddress. Você ainda poderia criar o método
copiaPara(List &lt? super INetAddress&gt)

Indicando que quer uma lista que aceite um tipo superior ao INetAddress.

Obrigado a todos pela ajuda!