Um passeio pelo Java 5 - Generics - Parte II
No artigo passado falei sobre: laço for melhorado, Autoboxing/Unboxing, enumerações (Enums), métodos com número de argumentos...
Um passeio pelo Java 5
Parte II – Generics
Interoperabilidade com Código Legado
Considere a atribuição:
Collection c = new ArrayList();
Ela é permitida para garantir um certo grau de compatibilidade entre as versões da linguagem (afinal todo código Java escrito até hoje foi sem generics). Quando um tipo genérico como Collection é utilizado sem parâmetro de tipo é chamado um raw type. Porém, o uso deste artifício deve ser feito com muito cuidado!
Vejamos seus desdobramentos na linha abaixo:
c.add(new Integer(10)); //unchecked warning
O compilador não acusará erro mas emitirá um aviso. O aviso é necessário porque o compilador não é capaz de garantir a corretude do resultado. Mas a possibilidade de se incluir um inteiro numa lista de strings não deveria ser um erro? Teoricamente sim, mas, na prática, se é necessário que código genérico opere com código legado isto deve ser permitido. Vejamos outro exemplo:
ArrayList fl = c; //unchecked warning
Dica
Dica 2. Quando usar métodos genéricos ou wildcards?
Use wildcard quando na assinatura do método o tipo paramétrico aparecer somente uma vez, ou seja, não há interdependência de tipos (nem de retorno, nem de argumentos).
public boolean containsAll(Collection
c)
public boolean containsAll(Collection
c)
Qual a melhor opção para os métodos acima? O parâmetro T está sendo usado neste caso apenas para prover polimorfismo e permitir que o método seja invocado com tipos diferentes. Use wildcards quando precisar representar tipos flexíveis, que é o que pretende-se expressar aqui.
“Métodos genéricos permitem que os tipos paramétricos sejam utilizados para expressar dependências entre tipos de argumentos e/ou seu tipo de retorno. Se não existe tal dependência um método genérico não deve ser empregado.”
public T
getSomething (T something) {…}
public static void
copy (List dest, Listextends T> src) {…}
Estes métodos são exemplos de dependência do
parâmetro de tipo. No primeiro, a dependência está no tipo de retorno (sim, o
tipo de retorno pode ser variável !!) e no segundo a lista de origem (src) deve
ser de algum subtipo da lista de destino (dest).
O compilador não consegue garantir que c contenha apenas objetos Float e emite novamente um aviso. Fica a cargo do programador satisfazer os contratos estabelecidos e assim, por exemplo, prezar para que contenha uma coleção de números.
Chamar código legado a partir de código genérico é intrinsecamente perigoso! Uma vez que você mistura código genérico com código não genérico todas as garantias de segurança que usualmente um código genérico provê são perdidas. Entretanto, ainda assim é melhor do que não usar código genérico algum.
Casts, Arrays e Literais de Classe
O que o seguinte código irá imprimir?
List l1 = new ArrayList();
List l2 = new ArrayList();
System.out.println(l1.getClass() == l2.getClass());
Você pode ficar tentado a dizer que será false mas na verdade é true. Todas as instâncias de uma classe genérica compartilham a mesma meta-classe (ou run-time class) independentemente dos valores atuais dos seus parâmetros de tipo (tal como ocorre com classes não genéricas). Uma conseqüência disso é que em variáveis e métodos estáticos, por serem compartilhados por todas as instâncias, não pode haver referências a parâmetros de tipo.
Uma outra implicação é que não faz sentido perguntar para uma instância se esta é uma instância de um tipo particular. O código:
Collection cs = new ArrayList();
if(cs instanceof Collection) {…} //ilegal!!
é ilegal, bem como fazer os casts
Collection str = (Collection) cs; // unchecked warning
ou
T badCast(T t, Object o) {return (T) o;} //unchecked warning
Variáveis de tipo não existem em tempo de execução. Em ambos os casos o compilador não pode garantir a conversão.
Não é permitida, por questões de segurança, a criação de arrays de tipos genéricos. Então as seguintes tentativas não são válidas
List[] L1 = new List[10]; //Erro – tentativa de criar um array de genéricos
List[] L2 = new List[10]; //Erro – tipos incompatíveis
List[] L3 = new List[10]; //Erro – tentativa de criar um array de genéricos
A única opção possível é a criação de (unbounded) wildcard arrays. Por exemplo
List[] lsa = new List[10]; //OK - array de unbounded wildcard
lsa[0] = new ArrayList();
lsa[1] = new LinkedList();
List li = new ArrayList();
li.add(new Integer(10));
lsa[3] = li;
Integer xx = (Integer) lsa[3].get(0); //É necessário um cast
Dica
Dica
Generics são implementados pelo compilador através de um mecanismo de conversão chamado erasure. Pode-se pensar neste como uma tradução source-to-source onde, basicamente, todas as informações de tipos genéricos são “apagadas“. Toda informação de tipo entre <> é removida, então, por exemplo, List é convertido para List. Os outros usos dos parâmetros de tipo dentro do código são substituídos (geralmente) por Object. Por fim, sempre onde o código resultante não estiver com o tipo correto um cast para o tipo apropriado é inserido.
Esse funcionamento explica porque uma
classe genérica é compartilhada por todas as suas instâncias e também porque
operações de verificação ou conversão de tipos genéricos não funcionam.
A classe java.lang.Class é genérica agora e é um bom exemplo de aplicação de generics além de coleções. Por exemplo, o tipo String.class é Class e o tipo Calendar.class é Class. Um dos principais benefícios é a utilização do literal de classe como fábrica de objetos e com a versão genérica isto ficou mais seguro pois pode-se obter um tipo preciso. Para entender melhor a diferença compare as duas versões abaixo:
Collection emps = sqlUtility.select(EmpInfo.class, “select * from emps”);
…
public static Collection select(Class c, String query)
{
Collection result = new ArrayList();
/* Executa a consulta */
for(/* Itera no resultset */)
{
Object item = c.newInstance(); //Retorna um Object
/* Utilize reflexao para setar os atributos do objeto baseado nos registros do banco*/
result.add(item); //Povoa a coleção com Objects
}
return result;
}
Collection emps = sqlUtility.select(EmpInfo.class, “select * from emps”);
…
public static Collection select(Class c, String query)
{
Collection result = new ArrayList();
/* Executa a consulta */
for(/* Itera no resultset */)
{
T item = c.newInstance(); //Retorna um EmpInfo
/* Utilize reflexao para setar os atributos do objeto baseado nos registros do banco*/
result.add(item); // Povoa a coleção com o tipo específico
}
return result;
}

Space do autor

Estudo comparativo entre banco de dados IBM Informix e Microsoft SQL


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