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

7 respostas
Y

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.

7 Respostas

Sami_Koivu

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

ViniGodoy

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:

LPJava
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:
List<Dog> my = ArrayList<Animal>();

isso nao é permitido.. em tempo de compilação.. entao usa a sintaxe do coringa

bom eu entendi assim segundo o cap 7 da kathy.. se eu tiver errado alguem me corrigi eheh!

Y

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!

Ironlynx

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

ViniGodoy

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.

Y

Obrigado a todos pela ajuda!

Criado 8 de janeiro de 2007
Ultima resposta 10 de jan. de 2007
Respostas 7
Participantes 5