Um passeio pelo Java 5 - Generics - Parte I
No artigo passado falei sobre: laço for melhorado, Autoboxing/Unboxing, enumerações (Enums), métodos com número de argumentos variável (Varargs) e Static Import. Nesta seqüência...
Estatísticas:










votos: 5
Serviços:

Um passeio pelo Java 5 - Generics - Parte I
Introdução
A última versão mais importante da linguagem Java havia sido a 1.2. De lá para cá novas versões apenas revisaram algumas características e corrigiram bugs. A versão 1.5 (Java 5 como vem sendo chamada), no entanto, é outro marco! Novas características foram incluídas na linguagem tornando-a mais robusta e fácil de usar. A JVM também sofreu melhorias e está mais rápida e confiável.
No artigo passado falei sobre: laço for melhorado, Autoboxing/Unboxing, enumerações (Enums), métodos com número de argumentos variável (Varargs) e Static Import. Nesta seqüência, apresento a mais significativa das mudanças ocorridas, que é a possibilidade de se criar classes e métodos parametrizados (semelhante às templates do C++). Uma classe ou método paramétrico está apto a ser invocado com tipos diferentes. Sendo assim, é possível definir uma variável dentro de uma classe ou o argumento de um método como um tipo paramétrico e apenas quando estes forem efetivamente utilizados este tipo será definido pelo usuário. Esta é uma tremenda flexibilidade para codificação e possibilita criar soluções mais genéricas e resumir muito código, o que significa menos necessidade de manutenção. Toda a API padrão da linguagem (todas as classes que implementam coleções por exemplo) foi refeita para tirar proveito destas facilidades.
O conceito de classe genérica (generics) é simples porém os detalhes para sua correta utilização são muitos. Espero ao final desse texto ter conseguido explicar de forma clara seus principais pontos.
Classes Genéricas
Considere o código abaixo:
public interface List
{
void add(E x);
Iterator
}
public interface Iterator
{
E next();
boolean hasNext();
}
As duas interfaces são definidas com o auxílio de parâmetros de tipos formais (formal type parameters), evidenciados pelas letras embutidas entre os sinais <>. Tipos paramétricos podem ser usados no código genérico de forma muito semelhante aos tipos convencionais. Um exemplo de utilização do código acima pode ser:
List
myList.add(new Integer(1));
Integer x = myList.iterator().next();
Até o Java 4 existiam apenas listas de Objects e para recuperar qualquer coisa diferente disso era necessário fazer um type cast. Agora pode-se definir a priori qual o tipo dos dados que uma lista (ou qualquer outra coleção) irá armazenar. Uma lista de inteiros receberá apenas inteiros, nada além disso. As conseqüências disso são várias:
· Não é preciso fazer cast para recuperar elementos já que o tipo correto de retorno já está especificado.
· O código fica mais robusto pois agora o compilador pode checar os tipos envolvidos (type safe) o que não acontecia quando um cast era necessário.
· Menos erros em tempo de execução.
· Exibe naturalmente um certo grau de reaproveitamento de código já que códigos genéricos são feitos para serem utilizados com tipos diferentes de dados.
Um ponto muito importante sobre generics está relacionado à subtipos. Considere o exemplo:
List
List
O que você acha do código acima? Já que String deriva de Object parece razoável supor que uma lista de strings também é um subtipo de uma lista de objetcs e sendo assim a atribuição acima estaria correta. Pois bem, considere esta continuação
lo.add(new Object());
String s = ls.get(0);
Na primeira linha um Object foi inserido na lista e na segunda este foi recuperado e atribuído a uma String, o que é um erro! Quando fizemos a variável lo receber ls esta deixou de ser uma lista apenas de strings e abriu a possibilidade de inserirmos qualquer tipo de objeto. Definitivamente este não é o tipo de comportamento que esperamos! É claro que atribuições como a acima não são permitidas e o compilador irá emitir uma mensagem de erro quando encontrá-las.
Dica 1.
Se Circle
é uma subclasse (ou subinterface)
de Shape
e G é uma declaração genérica
qualquer
então não é verdade que G
também um
subtipo de G
noção
inicialmente difícil de absorver pois vai
contra
nossa intuição natural.
Wildcards
A decisão acima é muito restritiva. Considere a necessidade de imprimir o conteúdo de uma coleção arbitrária e a tentativa de implementá-la exposta no método abaixo
void printCollection(Collection
{
for(Object e : c)
{
System.out.println(e);
}
}
Como vimos, ao contrário da intuição inicial, esse não é um método apto a receber qualquer tipo de coleção, mas sim exclusivamente coleções que contêm objetos do tipo Object. Para representar uma coleção de qualquer coisa devemos usar o caractere ? da seguinte forma: Collection. Isto é chamado de wildcardtype. O novo código utilizando wildcard é
void printCollection(Collection c)
{
for(Object e : c)
{
System.out.println(e);
}
}
o qual pode ser chamado com qualquer tipo de coleção. Observe que o conteúdo da coleção c é atribuído a um Object. O wildcard é um tipo desconhecido, mas como temos certeza que será um objeto é seguro atribuí-lo a uma variável do tipo Object.
Considere uma aplicação gráfica que desenha superfícies (shapes) tais como retângulos e círculos. Para representar esses componentes no programa podemos definir a seguinte hierarquia de classes.
Um método conveniente seria um para desenhar uma coleção de superfícies tal como
public void drawAll(List
{
for(Shape s: shapes)
{
s.draw(this);
}
}
O problema aqui (novamente) é que esse método só pode ser invocado recebendo como parâmetro uma List
public void drawAll(Listextends Shape> shapes)
{
for(Shape s: shapes)
{
s.draw(this);
}
}
Listextends Shape> é um exemplo de bounded wildcard. O ? representa, como antes, um tipo desconhecido. Entretanto, neste caso, sabemos que esse tipo é um Shape ou um subtipo deste. A desvantagem de não ser possível modificar uma variável com tipo desconhecido permanece.
Métodos Genéricos
Declarações de métodos também podem ser genéricas, isto é, parametrizadas por um ou mais parâmetro de tipo. Imagine como fazer um método que recebe um array de objetos e uma coleção e deve acrescentar todos os objetos do array dentro da coleção. Uma tentativa pode ser
public void fromArrayToCollection(Object[] a, Collection c)
{
for(Object o : a)
{
c.add(o); //Erro de compilação!
}
}
A opção de fazer Collection para ser possível receber uma coleção arbitrária foi correta, porém, tornou a coleção c apenas para leitura. Se reformularmos este método como um método genérico podemos resolver esse problema:
public
{
for(T o : a)
{
c.add(o);
}
}
Podemos chamar este método com qualquer tipo de coleção cujos elementos sejam um supertipo dos elementos do array. Por exemplo:
Object[] objectArray = new Object[100];
Collection
//Suponha as seguintes invocações do método fromArrayToCollection
fromArrayToCollection( Object, Object ); //Infere ser Object
fromArrayToCollection( String, Object ); //Infere ser Object
fromArrayToCollection( Integer, Number ); //Infere ser Number
fromArrayToCollection( Number, Object ); //Infere ser Object
fromArrayToCollection( Number, String ); //Erro de compilação!
Nas quatro primeiras chamadas do método o compilador pôde inferir um tipo válido para T, isto é, um tipo que é supertipo do outro. Perceba que não é necessário especificar explicitamente um tipo para um método genérico. O compilador irá inferir o tipo apropriado baseado nos tipos atuais. Na última chamada o método recebe como argumento um array de números e uma coleção de strings. Como String não é um supertipo de Number (e vice-versa) o compilador não consegue concluir um tipo válido e acusa erro.




Acoplamento fraco x Herança

Introdução ao Processo Unificado

Um passeio pelo Java 5 - Generics - Parte I

Um passeio pelo Java 5 - Generics - Parte II

Um passeio pelo Java 5 - Principais mudanças

Principais padrões J2EE para a construção de aplicações não distribuídas – Parte IV

Principais padrões J2EE para a construção de aplicações não distribuídas – Parte III

Principais padrões J2EE para a construção de aplicações não distribuídas – Parte II

Principais padrões J2EE para a construção de aplicações não distribuídas - Parte II

Principais padrões J2EE para a construção de aplicações não distribuídas - Parte II



Curso Online - Introdução ao AJAX Reverso

(tutorial) Aprenda a persistir dados em suas aplicações Java utilizando Hibernate

Desenvolvendo uma aplicação completa utilizando Swing

Introdução ao JDBC

Java Web: Saiba como Desenvolver Aplicações utilizando Spring, Hibernate e JSF na Prática

OLAP com o SQL Server

Introdução à Engenharia de Requisitos

Curso OnLine WebDesign - Desenvolvendo o layout de um WebSite passo a passo




[vídeo] Alterando dados no arquivo XML

[vídeo] Array no ViewData: Curso ASP.NET MVC 2.0 com Visual Studio 2010 - Parte 14

Mineração de Repositórios de Software: A Computação ajudando à Computação.

Boas-vindas

Boas-vindas

Mineração de Repositórios de Software: A Computação ajudando à Computação.

[vídeo] Teste Automatizado: Codificação do UserTest - Curso JEE e JSE – Loja Virtual Completa – Parte 17

[vídeo] MD5 com Delphi: Usando o Método Locar - Curso Aplicação Financeira Delphi 2009 e MySQ – Parte 32

[vídeo] MD5 com MySQL: Utilizando a função para Logar - Curso Aplicação Financeira Delphi 2009 e MySQL – Parte 31



Você está em:





Conheça os planos de créditos DevMedia e visualize esse post agora mesmo!

