Genéricos (postagem 2 de 2)
Autor: Gustavo | Publicado: 2015-04-01T09:30:00-03:00


Polimorfismo
com Genéricos



&nbsp; &nbsp; Por
estarmos lidando sempre com objetos muitos
programadores confundem a declaração
de uma
lista com genéricos
utilizando o
polimorfismo. Uma vez que é 
possível realizar a
declaração com
arrays e variáveis comuns, poque não é possível realizar
utilizando genéricos? 




Exemplo:



Campeonatos
surf
= new
Surf();//OK

Campeonatos
ksurf
= new
KiteSurf();//OK

Campeonatos
wsurf
= new
WindSurf();//OK

Campeonatos
camp
= new
Campeonatos();//OK



Campeonatos[]
campS
= new
Surf[4];//OK

Campeonatos[]
campKS
= new
KiteSurf[4];//
OK

Surf[]
surf
= { new
Surf(), new
Surf(), new
Surf() };//
OK

KiteSurf[]
kiteSurf
= { new
KiteSurf(), new
KiteSurf(), new
KiteSurf() };//
OK

WindSurf[]
windSurfs
= { new
WindSurf() };//
OK



class
Campeonatos {

 public
void
preRequisistos() {

  System.out.print("necessário
prancha ");

 }

}



class
Surf extends
Campeonatos {

 public
void
preRequisistos() {

  super.preRequisistos();

  System.out.println("e
ondas!");

 }

}



class
KiteSurf extends
Campeonatos {

 public
void
preRequisistos() {

  super.preRequisistos();

  System.out.println(",
ventos e pipa!");

 }

}



class
WindSurf extends
Campeonatos {

 public
void
preRequisistos() {

  super.preRequisistos();

  System.out.println(",
ventos e vela!");

 }

}



&nbsp; &nbsp;&nbsp;Vemos
acima 4 classes a classe Campeonatos é a pai e as
outras três (Surf,
KiteSurf, WindSurf)
são o que dizemos de classes filhas. No
início  do código acima vemos nas quatro
primeiras linhas declarações de
variáveis de instância de uma classe usando polimorfismo o que para
nós é algo comum assim como ja vimos as outras quatro declarações
de um array usando o polimorfismo, mas quando chegamos na hora de
declarar um arrayList, nos deparamos com um erro que a princípio não
esperavamos que fosse ocorrer. Até
porque o próprio arrayList
se utiliza
do polimorfismo no momento em que sua delcaração utiliza como
classe de referência a interface List
e a classe de instancia
a ArrayList. Porque
então não poderia utilizar genéricos da mesma maneira?

&nbsp; &nbsp;&nbsp;Explicarei
mais adiante o motivo, mas antes farei uma abordagem
sobre
array que
fizemos
no
código acima e que esperávamos que funcionasse
com conjuntos de tipo específicos. 


&nbsp; &nbsp;&nbsp;Vimos
erros de compilação
que
ocorrem durante a declaração de um conjunto com a utilização de
genéricos junto com polimorfismo, este
erro também poderá ser visto na
passagem de parâmentro em métodos.
Conforme mostraremos no código abaixo “não será possível” a
passagem de parâmetros de um conjunto ArrayList&lt;Surf&gt;
a um método que possui como parâmetro ArrayList&lt;Campeonatos&gt;
e
é
claro que utilizamos as aspas acima pois isto
não poderia ficar tão engessado,
existem
maneiras de como ela porderá aceitar
como
parâmentro uma lista de subclasses mas isso eu mostrarei mais
adiante por hora demonstro esse código abaixo para terem uma idéia
do  que estou falando e em seguida darei mais detalhes.



//
List&lt;Campeonatos&gt; campeonatos1 = new ArrayList&lt;Surf&gt;();
//ERRO Compilacao

//
List&lt;Campeonatos&gt; campeonatos = new ArrayList&lt;WindSurf&gt;();
//ERRO Compilacao

//
List&lt;Campeonatos&gt; campeonatos2 = new ArrayList&lt;KiteSurf&gt;();
//ERRO Compilacao

List&lt;Surf&gt;
campeonatos3
= new
ArrayList&lt;Surf&gt;();//
OK 

List&lt;KiteSurf&gt;
campeonatos4
= new
ArrayList&lt;KiteSurf&gt;();//
OK

List&lt;WindSurf&gt;
campeonatos5
= new
ArrayList&lt;WindSurf&gt;();//
OK

List&lt;Campeonatos&gt;
campeonatos6
= new
ArrayList&lt;Campeonatos&gt;();//
OK



Campeonatos[]
campS
= new
Surf[4];

Campeonatos[]
campKS
= new
KiteSurf[4];

Surf[]
surf
= { new
Surf(), new
Surf(), new
Surf() };

KiteSurf[]
kiteSurf
= { new
KiteSurf(), new
KiteSurf(), new
KiteSurf() };

WindSurf[]
windSurfs
= { new
WindSurf() };





Eventos
eventos
= new
Eventos();

eventos.comecarCampeonatos(campeonatos3);//
ERRO DE COMPILACAO

eventos.comecarCampeonatos(campeonatos4);//
ERRO DE COMPILACAO

eventos.comecarCampeonatos(campeonatos5);//
ERRO DE COMPILACAO

eventos.comecarCampeonatos(campeonatos6);//
OK

eventos.realizarEventos(surf);//OK

eventos.realizarEventos(kiteSurf);//OK

eventos.realizarEventos(windSurfs);//OK





class
Eventos {

 public
void
comecarCampeonatos(List&lt;Campeonatos&gt;
campeonatos){

  for(Campeonatos
c:
campeonatos){

   c.preRequisistos();

  }

 }

  public
void
realizarEventos(Campeonatos[] listaDeEventos)
{

  System.out.println("array");

  for
(Campeonatos c
: listaDeEventos)
{

   c.preRequisistos();

  }

 }



}



&nbsp; &nbsp;&nbsp;Conforme
falado anteriormente, vemos que única variável campeonatos6
aceita pelo compilador para passagem de parâmentro do método
comecarCampeonatos, isso
porque ela é a única que possui declaração idêntica a do
parâmetro do método.

&nbsp; &nbsp;&nbsp;O
que mais intriga é o motivo que faz com que seja possível fazer
esta passagem por arrays e não com arrayList e a razão é bem
simples, o compilador faz o tratamento entre arrays e ArrayList
diferentes apesar de terem certas familiaridades.

&nbsp; &nbsp;&nbsp;Comecarei
falando das suas familiaridades que são mais simples para depois
vermos os tratamentos diferentes.

&nbsp; &nbsp;&nbsp;Tanto
array quanto ArrrayList possuem benefícios de serem declararados por
um supertipo mais abstrato como:




O
 array poderá armazenar objetos de vários tipos de subTipos do
 superTipo


Manipulação
 do array assuminto que tudo dele pode responder a sua superClasse.


Aproveitamento
 de código e facilitando na fácil alteração do código.





&nbsp; &nbsp;&nbsp;Isso
faz com que tanto Arrays quanto ArrayList possam armazenar seus
subTipos conforme exemplo abaixo:



  ArrayList&lt;Surf&gt;
surf2
= new
ArrayList&lt;Surf&gt;();

  surf2.add(new
Surf());

  surf2.add(new
Surf());

  surf2.add(new
Surf());

  ArrayList&lt;KiteSurf&gt;
kiteSurf2
= new
ArrayList&lt;KiteSurf&gt;();

  kiteSurf2.add(new
KiteSurf());

  kiteSurf2.add(new
KiteSurf());

  kiteSurf2.add(new
KiteSurf());

  ArrayList&lt;WindSurf&gt;
windSurfs2
= new
ArrayList&lt;WindSurf&gt;();

  windSurfs2.add(new
WindSurf());

  ArrayList&lt;Campeonatos&gt;
camps
= new
ArrayList&lt;Campeonatos&gt;();

  camps.add(new
Surf());

  camps.add(new
KiteSurf());

  camps.add(new
WindSurf());

  camps.addAll(windSurfs2);

  camps.addAll(surf2);

  camps.addAll(kiteSurf2);



  Campeonatos[]
campS
= new
Campeonatos[3];

  campS[0]
= new
Surf();

  campS[1]
= new
WindSurf();

  campS[2]
= new
KiteSurf();

  Campeonatos[]
campKS
= { new
Surf(), new
WindSurf(), new
            KiteSurf() };



&nbsp; &nbsp;&nbsp;Com
a demontração acima vemos que é possível arrayList armazenar
subclasses em um conjunto a partir de uma superclasse especificada. O
código acima compila e executa normalmente. 


&nbsp; &nbsp;&nbsp;Voltando
a questão que questionamos a pouco quando os problemas relacionados
a polimorfismo, vemos  que neste momento os conjuntos arrayList
especificados como arrayList não geram erros durante a compilação
comprovando que é possível trabalhar com um conjunto de uma
superClasse que por  sua vez poderá obter várias subclasses
internamente.

&nbsp; &nbsp;&nbsp;A
verdade é que o compilador trata de forma diferente os conjuntos
array e arrayList. Em quanto o primeira não possui um tratamento e
não gera erro ao trabalharmos com vários tipos de elementos
diferentes (Surf, WindSurf e KiteSurf) em um mesmo conjunto,  o mesmo
não ocorrer trabalhando com arrayList. A partir da classificação
por meio dos genéricos o compilador só adiciona um elemento
(subclasse) em um conjunto caso o programador informe que realmente
quer que isso ocorra. Desta forma não  irá tirar os benefícios de
uma programação bem estruturada por polimorfismo. A princípio o
compilador irá trabalhar em um conjunto esperando somente um tipo de
elemento. Para que ele aceite outros tipos de elementos aceitos pelo
polimorfismo o programador deverá informar ao compilador que irá
realizar tal procedimento utilizando o caracter (?) também chamado
de coringa.



 public
void realizarEventosC(
ArrayList&lt;? extends
Campeonatos&gt;
            listaDeEventos)
{

  System.out.println("arrayList");//
nao
se
pode
adicionar
aqui

  for
(Campeonatos c
: listaDeEventos)
{

   c.preRequisistos();

  }

 }



 public
void
realizarEventosSurf(ArrayList&lt;? super
Surf&gt; listaDeEventos)
{

  System.out.println("arrayList");

  listaDeEventos.add(new
Surf());



 }

&nbsp; &nbsp;&nbsp;Conforme
exemplo acima,vemos que é possível a utilização do coringa de 2
formas diferentes, no primeiro método realizaEventosC informa
que qualquer elemento que extenda
a classe Campeonatos poderá ser passada em quanto o segundo método
realizarEventosSurf
informa ao compilador que qualquer superclasse
que entre no relaciomento É-UM
com
a
classe Surf
e
quando digo que é qualquer um relacionamento que se enquadra é por
porder utilizar inclulsive interfaces. Não existe como colocar em um
parâmetro de um método a expressão &lt;?
implements UmaInterface&gt; por
mais que possa parecer estranho é a palavra reservada extends que
será utilizada neste momento.

&nbsp; &nbsp;&nbsp;O
coringa pode inclusive ser utilizado para informar que o parâmentro
poderá ser uma lista de qualquer tipo de elemento cconforme abaixo:



 public
void
metodoQuePassaComoParamsListaDeQQElemento(List&lt;?&gt; elemento){



&nbsp; &nbsp;&nbsp;Outra
informação importante é que apesar de estarmos focando em
parâmentros de métodos todas estas formas de especificar um
conjunto que possua subclasses, superclasses ou de qualquer elemento,
poderá ser usado também para um retorno de um método específico
da seguinte foram:



public
ArrayList&lt;? extends
Campeonatos&gt; realizarEventosC()
{...



public
ArrayList&lt;?
super
Surf&gt;
 realizarEventosSurf() {...



&nbsp; &nbsp;&nbsp;A
única ressalva para a utilização do croringa é em uma declaração
de uma variável onde o caractere de interãrogaço não poderá
estar presente na parte de instanciação. 


Exemplos:



ArrayList&lt;?
super
Surf&gt; listaDeEventos
= new
ArrayList&lt;Campeonatos&gt;();//OK



ArrayList&lt;Campeonatos&gt;
listaDeEventos
= new
ArrayList&lt;&lt;? super
Surf&gt;&gt;();//Erro



Declarações
Genéricas



&nbsp; &nbsp;&nbsp;Sempre
que nos depararmos com uma declaração genérica, depararemos com
duas formas diferentes de marcadores, a letra “e” normalmente
maiúscula entre os operadores de menor que e maior que &lt;E&gt; que
significa elemento e a letra “t” normalmente em sua forma
maiúscula, &lt;T&gt; que significando tipo.

&nbsp; &nbsp;&nbsp;Estas
convenções servem para deixar como abstrato o elemento ou tipo que
o desenvolvedor utilizará quando necessitar em sua declaração
genérica a utilização destas convenções ajuda inclusive na
utilização junto com uma IDE que preenche o elemento ou tipo da
classe de acordo com o que tem desenvolvido. 




Exemplo:





class
Campeonatos&lt;T&gt; {



 private
List&lt;T&gt; competidores;//podem
ser
surfistas,
windsurfistas,
kiteSurfistas

 private
int
numeroDeCompetidores;



 public
Campeonatos(int
numeroCompetidores,
List&lt;T&gt; competidores)
{

  //construtor
com
as mesmas
caracteristicas
surfistas,
windsurfistas,
   kiteSurfistas

  this.competidores
= competidores;

  this.numeroDeCompetidores
= numeroCompetidores;

 }

 public
T getCompetidor(){

  return
competidores.get(0);

 }

 public
void
setCompetidores(T competidor){

  competidores.add(competidor);

 }

 public
void
preRequisistos() {

  System.out.print("necessário
prancha ");

 }

}



Uma
vez com a classe feita vamos utiliza-la



List&lt;Surfista&gt;competidores=Surfista.mockSurfistas();//criamos
uma lista de
          //surfista

Campeonatos&lt;Surfista&gt;
campeonatoSurf
= new
Campeonatos(quatro,
competidores); 



//
buscar
um
competidor
qualquer

Surfista
s
= campeonatoSurf.getCompetidor();



campeonatoSurf.retornarCompetidores(s);



//será
que
da
pra
colocar
outro
tipo
de
competidor

competidores.add(new
KiteSurfista());//ERRO
de
compilacao



&nbsp; &nbsp;&nbsp;Ao
invés  de termos uma classe para cara tipo de competição, podemos
agora apenas ter uma classe genérica  do tipo competição onde os
competidores são passados de uma forma genérica.

&nbsp; &nbsp;&nbsp;Existem
várias formas de utilizarmos a parametrização mas devemos tomar
cuidado com a utilização excessiva desta funcionalidade tal como:



public
class
TestGeneric&lt;T&gt; {



 T
umaInstancia;

 T[]
arrayParametrizado;

 List&lt;T&gt;
listaParametrizada;



 public
TestGeneric(T umaInstancia,
T[] arrayParametrizado,

   List&lt;T&gt;
listaParametrizada)
{

  this.umaInstancia
= umaInstancia;

  this.arrayParametrizado
= arrayParametrizado;

  this.listaParametrizada
= listaParametrizada;

 }



 public
T getUmaInstancia() {

  return
umaInstancia;

 }



 public
void
setUmaInstancia(T umaInstancia)
{

  this.umaInstancia
= umaInstancia;

 }



 public
T[] getArrayParametrizado() {

  return
arrayParametrizado;

 }



 public
void
setArrayParametrizado(T[] arrayParametrizado)
{

  this.arrayParametrizado
= arrayParametrizado;

 }



 public
List&lt;T&gt; getListaParametrizada() {

  return
listaParametrizada;

 }



 public
void
setListaParametrizada(List&lt;T&gt; listaParametrizada)
{

  this.listaParametrizada
= listaParametrizada;

 }



}



Ou
até mesmo utilizando mais de uma parametrização em uma mesma
classe :



class
TestTresGeneric&lt;T,X,Z extends
Campeonatos&gt;
{



 T
umaInstancia;

 X
outraInstancia;

 Z
tipoDeCampeonato;



 public
TestTresGeneric(T umaInstancia,
X outraInstancia,

   Z
tipoDeCampeonato)
{

  this.umaInstancia
= umaInstancia;

  this.outraInstancia
= outraInstancia;

  this.tipoDeCampeonato
= tipoDeCampeonato;

 }



 public
T getUmaInstancia() {

  return
umaInstancia;

 }



 public
void
setUmaInstancia(T umaInstancia)
{

  this.umaInstancia
= umaInstancia;

 }



}




&nbsp; &nbsp;&nbsp;Achou
um absurdo? Mas sempre tem como piorar um pouco :) imagine agora mais
de uma variável parametrizada onde uma delas ainda faz parte de um
relacionamento É-Um de uma classe Campeonatos.



class
TestTresGeneric&lt;T,X,Z extends
Campeonatos&gt;
{



 T
umaInstancia;

 X
outraInstancia;

 Z
tipoDeCampeonato;



 public
TestTresGeneric(T umaInstancia,
X outraInstancia,

   Z
tipoDeCampeonato)
{

  this.umaInstancia
= umaInstancia;

  this.outraInstancia
= outraInstancia;

  this.tipoDeCampeonato
= tipoDeCampeonato;

 }



 public
T getUmaInstancia() {

  return
umaInstancia;

 }



 public
void
setUmaInstancia(T umaInstancia)
{

  this.umaInstancia
= umaInstancia;

 }



}



&nbsp; &nbsp;&nbsp;Aqui
finalizo nosso tri-post sobre genéricos, acredito que grande parte
do que usamos já foi dito sugestões fiquem a vontade :)




https://github.com/gmachado1/blog/tree/branche1/GenericosEConjuntos