Programação com genéricos

Propaganda
Programação com genéricos
Laboratório de Programação
Pedro Vasconcelos, DCC/FCUP
Fevereiro 2015
Tipos genéricos
• tipos genéricos permitem definir classes ou interfaces que são parameterizadas por outras classes
• foram acrescentados à linguagem Java na versão 5
• baseados no polimorfismo paramétrico de linguagens como Standard ML
ou Haskell
• permitem que as coleções sejam polimórficas
(e.g. utilizar listas, filas, etc. com vários tipos)
• permitem detetar mais erros de tipos em compilação
Exemplo: listas
Queremos implementar listas ligadas de inteiros ou de strings.
Em Java 1.0 há duas alternativas:
1. duas classes separadas LinkedIntList e LinkedStringList
2. uma só classe LinkedList cujos elementos são Object
A opção 1 causa duplicação de código.
A opção 2 obriga a coerções de tipos e potencial perda de segurança de
tipos em tempo de compilação
Em Java 5: os tipos genéricos resolvem ambos problemas.
1
LinkedList sem genéricos
LinkedList list = new LinkedList();
// criar uma lista vazia
list.add("hello");
// adicionar um elemento
String s = list.get(0);
// erro de tipos em compilação
// método get retorna Object
String s = (String) list.get(0);
// resolvemos o erro: coerção explicita
Integer n = (Integer) list.get(0);
// erro de tipos na execução
LinkedList com genéricos
LinkedList<String> list
= new LinkedList<String>();
// criar uma lista vazia
list.add("hello");
// adicionar um elemento
String s = list.get(0);
// não é necessária coerção
Integer n = list.get(0);
// erro de tipos na compilação
Inferência de tipos
Podemos omitir o parâmetro de tipo dum construtor e escrever apenas <>
(“diamond”) se o tipo puder ser inferido do contexto.
LinkedList<String> = new LinkedList<>();
// parâmetro <String> inferido
NB: se omitirmos o operador <> criamos uma coleção pré-genéricos (i.e. de
Object):
2
LinkedList<String> = new LinkedList();
// warning: unchecked conversion
Instanciação de parâmetros
O parâmetro de tipo pode ser substituido (instanciado) por qualquer classe
(inclusivé outra instância de um tipo genérico).
LinkedList<Integer> list1;
// lista de inteiros
LinkedList<String> list2;
// lista de strings
LinkedList<LinkedList<Integer>> list3;
// lista de listas de inteiros
Instanciação de parâmetros (2)
Este mecanismo corresponde exatamente ao “polimorfismo paramétrico” em
Haskell.
Os tipos correspondentes são:
list1 :: [Int]
-- lista de inteiros
list2 :: [String]
-- lista de strings
list3 :: [[Int]]
-- lista de listas de inteiros
Classes wrapper
Porquê LinkedList<Integer> em vez de LinkedList<int>?
. . .
Resposta: porque apenas podemos colocar objetos dentro duma coleção e os
valores primitivos não são objetos!
3
Classes wrapper (cont.)
• Integer é uma classe wrapper que apenas “embrulha” um inteiro como
um objeto
• necessária para colocar inteiros dentro de coleções genéricas
• existem wrappers para todos os outros tipos primitivos (Float, Double,
etc.)
Integer a = new Integer(42);
// int para Integer ("boxing")
Integer b = Integer.valueOf(42);
// alternativa (forma preferida)
int c = a.intValue() + b.intValue();
// Integer para int ("unboxing")
Coleções genéricas
As bibliotecas de Java incluêm classes para estruturas de dados que implementam
coleções genéricas.
Alguns exemplos (de java.util.*):
LinkedList<E> listas duplamente ligadas
ArrayList<E> listas implementada como arrays de tamanho redimensionado
automaticamente
Stack<E> pilhas LIFO (“last-in, first-out”)
O parâmetro de tipo E representa o tipo dos elementos na coleção.
Interfaces a coleções
Além das classes que implementam estruturas concretas, temos também interfaces
genéricas que abstraiem os detalhes de implementação.
Exemplos:
List<E> interface genérico para listas (e.g. ArrayList ou LinkedList)
Deque<E> interface genérico para filas com acesso no início e fim
Set<E> interface genérico para conjuntos (e.g. TreeSet ou HashSet)
4
Interfaces genéricos
Os genéricos também são usado em interfaces para operações comuns.
Dois exemplos:
Comparable<T> efetuar comparações numa ordem total (i.e. <, = ou >)
Iterator<T> efetuar iteração sobre uma sequência de objetos
Interface Comparable
public interface Comparable<T> {
int compareTo(T o);
}
• define um método compareTo()
• a.compareTo(b) compara o objeto a com b
• o resultado é um inteiro que indica a ordem relativa:
<0
0
>0
a é menor do que b
a é igual a b
a é maior do que b
Exemplo
• uma classe para representar pessoas com atributos nome e idade
• implementar ordem comparando primeiro as idades e (se forem iguais) os
nomes (i.e. uma ordem lexicográfica)
Exemplo (cont.)
class Pessoa implements Comparable<Pessoa> {
private String nome;
private int idade;
...
// construtor omitido
public int compareTo(Pessoa outro) {
// comparar primeiro idades
int cmp = this.idade - outro.idade;
5
if (cmp == 0) {
// idades iguais: comparar nomes
cmp = this.nome.compareTo(outro.nome);
}
return cmp;
}
}
Exemplo (cont.)
Exercício: redefinir o método equals de forma consistente com compareTo.
Para quaisquer duas pessoas a, b tais que
a.compareTo(b) == 0
então
a.equals(b) == true
6
Download