Orientação a objetos com Java
Generics
Byron Leite e Bruno Fernandes
{byronleite,bjtf}@ecomp.poli.br
Agenda Geral
• Parte 07
– Exceções
• Parte 08
– Exceções
• Parte 09
– Classes úteis
– Generics
– Arquitetura em camadas
• Parte 10
Motivação
• Coleções em Java
– Coleções tratam SEMPRE tipos genéricos
(Object)
– É impossível, nas versões anteriores ao Java
1.5, garantir que objetos de tipos diferentes
não serão inseridos em uma coleção
– Quando se lê objetos de uma coleção, o uso
do cast é obrigatório, e sob risco de não se
estar convertendo o objeto para o tipo certo
Problema
• Analisando o código abaixo...
List lista =
new
ArrayList();
lista.add(
"EDUARDO"
);
// OK em compilacao
lista.add(
new
Integer(2));
// OK em compilacao
lista.add(
new
Double(2.2));
// OK em compilacao
// Agora...
// Se, mais na frente, alguem faz isso...
String elemento = (String)lista.get(1);
// Erro de execucao
• Se houvesse uma maneira de garantir em
tempo de compilação que, na dada lista,
só fosse possível se adicionar elementos
de um mesmo tipo (suponha String)??
Problema
• Ainda analisando o código abaixo...
List lista =
new
ArrayList();
lista.add(
"EDUARDO"
);
// OK em compilacao
lista.add(
new
Integer(2));
// OK em compilacao
lista.add(
new
Double(2.2));
// OK em compilacao
// Agora...
// Se, mais na frente, alguem faz isso...
String elemento = (String)lista.get(1);
// Erro de execucao
• E, se houvesse uma maneira de evitar o cast??
Se o Java “soubesse” que a dada lista é uma
lista de Strings, não haveria alternativa...O tipo
da variável que receberia o retorno do método
get TERIA que ser String
Solução
• No Java 5, é possível SUB-TIPAR alguns elementos que
trabalham com tipos abstratos
List<String> lista =
new
ArrayList<String>();
lista.add(
"EDUARDO"
);
// OK em compilacao
lista.add(new Integer(2));
// Erro de compilacao 1
lista.add(
new
Double(2.2));
// Erro de compilacao 2
// Agora...
// Se, mais na frente, alguem quiser acessar um elemento...
String elem0 = lista.get(0);
// Sem cast
Integer elem1 = lista.get(1); //
Erro de compilacao 3
Double elem2 = (Double)lista.get(1); //
Erro de compilacao 4
• O argumento entre <> é o tipo associado aos elementos
que serão manipulados através dos métodos da
interface List e que serão armazenados no objeto
ArrayList
Solução
• É como se, colocando o tipo entre <>, o
programador dissesse:
– Java, para esta lista que eu estou instanciando e
representando por esta variável, onde antes se
trabalhava com Object, passe a trabalhar APENAS
com o tipo que eu especificar entre <>
• Este mecanismo é chamado de Generics
• Aumenta a garantia de código robusto e sujeito
a erros de execução
Solução
• Nos erros de compilação 1 e 2
– O método add(String s) espera uma String
– Está se tentando passar um Integer e um
Double para uma String
– É o mesmo que: String s = new Integer(2);
• Integer não é sub-tipo de String
– É o mesmo que: String s = new Double(2.2);
Solução
• Nos erros de compilação 3 e 4...
– O tipo de retorno do método get é String
– Regra de compilação do cast:
• Tipo do cast igual ou sub-tipo do tipo da variável
de destino - OK
• Tipo do cast e tipo da variável ou do retorno de
origem na mesma hierarquia
– Tipo do cast: Integer
– Tipo do retorno de origem: String, pois o get volta uma
String
Percorrendo Coleções
• Analise o seguinte programa escrito da
forma tradicional...
List listaProd = new ArrayList();
long codigo = 0;
do {
codigo = KeyboardReader.readLong();
if (codigo > 0) {
String nome = KeyboardReader.readString();
double preco = KeyboardReader.readDouble(); Produto p = new Produto(codigo,nome,preco); listaProd.add(p);
}
} while (codigo > 0);
// Mostra os produtos incluidos na lista
for (int i=0;i<listaProd.size();i++) {
Produto p = (Produto)listaProd.get(i);
System.out.println("Codigo: "+p.getCodigo()); System.out.println("Nome : "+p.getNome()); System.out.println("Preco : "+p.getPreco()); }
Percorrendo Coleções
• Agora, analise o programa escrito com
Generics...
List<Produto> listaProd = new ArrayList<Produto>();
long codigo = 0;
do {
codigo = KeyboardReader.readLong();
if (codigo > 0) {
String nome = KeyboardReader.readString();
double preco = KeyboardReader.readDouble(); Produto p = new Produto(codigo,nome,preco); listaProd.add(p);
}
} while (codigo > 0);
// Mostra os produtos incluidos na lista
//for (Produto p : listaProd) {
for (int i=0;i<listaProd.size();i++) { Produto p = listaProd.get(i);
System.out.println("Codigo: "+p.getCodigo()); System.out.println("Nome : "+p.getNome());
Outras Aplicações
• Generics pode ser usado em outros
contextos
– Garantia de compatibilidade de tipos em
métodos que recebem parâmetros correlatos
– Rotinas genéricas podem indicar que SÓ
recebem coleções de uma
Generics em Classes e Intefaces
• Parametrizam a classe ou interface
• Método genérico:
public interface List<T> extends Collection<T> { ...
T get(int index); ...
}
public static <T> T getFirst(List<T> list) {
return list.get(0); }
Wildcards
• Permite um tratamento mais genérico dos
tipos passados a uma função
public void processElements(List<?> elements) { for(Object o : elements) {
System.out.println(o); }
}
/* Podemos atribuir um list de qualquer tipo a nosso método, pois ele tem um tipo desconhecido/genérico */
List<A> listA = new ArrayList<A>(); processElements(listA);
Extends Wildcards
public void processElements(List<? extends Fruit> elements) {
for(Fruit a : elements) {
System.out.println(a.getValue()); }
}
/* Podemos agorar passar nossas frutas diversas ao método processElements */
List<Apple> listApple = new ArrayList<Apple>(); processElements(listApple);
List<Orange> listOrange = new ArrayList<Orange>(); processElements(listOrange);
List<Strawberry> listStrawberry = new ArrayList<Strawberry>();