Fundamentos da Programação 14:
• Interfaces
• Listas e cadeias ligadas
• Iteradores
Apresentação 14 da unidade curricular de Fundamentos de Programação da Universidade Europeia. Alterações de Manuel Menezes de Sequeira sobre versão original por vários autores do DCTI do ISCTE-IUL, incluindo Luís Nunes, Ricardo Ribeiro, André Santos e o próprio Manuel Menezes de Sequeira.
2. Polimorfismo de subtipos
Classes e operações polimórficas
Herança
Ligação estática e ligação dinâmica
Classes abstractas e classes concretas
Operações abstractas
Análise, desenho e implementação
Dicionário do domínio
Conceitos
Conceitos concretos e conceitos abstractos
2013/2014 Fundamentos de Programação 2
3. Definem um comportamento que as classes podem
implementar
Contêm apenas constantes e declarações de
operações (e tipos embutidos)
Classes podem implementar interfaces
Uma classe pode implementar várias interfaces,
embora possa derivar apenas de uma outra classe
Uma interface pode estender (derivar de) outra
2013/2014 Fundamentos de Programação 3
4. public interface Comparable<Type> {
int compareTo(Type object);
}
public interface Queue<Item> {
Item element();
void add(Item item);
void remove();
}
2013/2014 Fundamentos de Programação 4
Operações apenas declaradas.
Não se define qualquer método.
Não é necessário usar o
qualificador abstract.
Operações públicas
por omissão.
Nota: A interface Queue é um
pouco diferente na biblioteca do
Java!
Interface genérica. Type é um
parâmetro. O correspondente
argumento tem de ser um tipo.
5. public final class Rational implements Comparable<Rational> {
private final int numerator;
private final int denominator;
…
@Override
public int compareTo(final Rational another) {
final int leftNumerator =
getNumerator() * another.getDenominator();
final int rightNumerator =
another.getNumerator() * getDenominator();
if (leftNumerator > rightNumerator)
return 1;
if (leftNumerator < rightNumerator)
return -1;
return 0;
}
…
}
2013/2014 Fundamentos de Programação 5
6. public final class Rational implements Comparable<Rational> {
private final int numerator;
private final int denominator;
…
public int compareTo(final Rational another){
…
}
…
}
Implementação
2013/2014 Fundamentos de Programação 6
«interface»
Comparable<Type → Rational>
Rational
Relação de
realização
Comparable<Type → Rational>
Rational
7. public class CustomerQueue implements Queue<Customer> {
…
}
public class MyQueueTester {
…
public static void main(final String[] arguments) {
Queue<Customer> customerQueue =
new CustomerQueue();
…
}
…
}
2013/2014 Fundamentos de Programação 7
Queue<Item → Customer>
CustomerQueue
8. Adjectivo denotando possibilidade de realizar
determinadas operações (e.g., Comparable)
Nome denotando conceito cujo
comportamento será implementado (e.g.,
Queue)
2013/2014 Fundamentos de Programação 8
Caso semelhante ao das classes abstractas, mas
1. não há qualquer herança de implementação (i.e.,
de atributos não constantes e métodos) e
2. uma classe que implemente a interface pode
simultaneamente implementar outras interfaces.
9. Precisa-se de classes semelhantes
Com operações semelhantes
Com atributos semelhantes
Variando apenas em alguns tipos (e.g., o tipo dos
elementos a guardar em diferentes listas)
É necessário definir essas classes
separadamente?
2013/2014 Fundamentos de Programação 9
10. public class ArrayList {
private Object[] items;
…
}
2013/2014 Fundamentos de Programação 10
- items
*
ObjectArrayList
Room
11. public class ArrayList<Item> {
private Item[] items;
…
}
2013/2014 Fundamentos de Programação 11
ArrayList<Item → Room> Room
*
- items
ArrayList
Item
«bind» <Item → Room>
- items : Item [*]
Notação UML para classes
genéricas (também conhecidas
por modelos ou templates).
Classe genérica. Item é um
parâmetro. O correspondente
argumento tem de ser um tipo.
12. public class Floor {
private ArrayList<Room> rooms =
new ArrayList<Room>();
public Floor(final int numberOfRooms) {
for (int roomNumber = 1;
roomNumber != numberOfRooms + 1;
roomNumber++)
rooms.addFirst(new Room(roomNumber));
}
public void show() {
while (rooms.hasNext())
out.println(rooms.next());
}
}
2013/2014 Fundamentos de Programação 12
Inserir novo elemento no início ou a meio
da lista implica «empurrar» elementos
existentes para «fazer espaço».
Para fazer isto o
que é preciso?
13. public class LinkedList<Item> {
private Node<Item> firstNode = null;
private Node<Item> currentNode = null;
…
private static class Node<Item> {
private Node<Item> nextNode;
private Item item;
private Node(final Node<Item> nextNode,
final Item item) {
this.nextNode = nextNode;
this.item = item;
}
}
…
}
2013/2014 Fundamentos de Programação 13
Classe embutida (nested)
privada. A classe
LinkedList<Item> tem
acesso aos membros
privados de Node<Item>.
15. public class Floor {
private LinkedList<Room> rooms =
new LinkedList<Room>();
public Floor(final int numberOfRooms) {
for (int roomNumber = 1;
roomNumber != numberOfRooms + 1;
roomNumber++)
rooms.addFirst(new Room(roomNumber));
}
public void show() {
while (rooms.hasNext())
out.println(rooms.next());
}
}
2013/2014 Fundamentos de Programação 15
Inserir novo elemento no início ou no meio
da lista não implica «empurrar» elementos
existentes!
26. 2013/2014 Fundamentos de Programação 26
LinkedList
Item
Node
Ite
m
- item: Item
nextNode
firstNode
currentNode 0..1
0..1
0..1
0..1
Esta solução não
permite percorrer a
lista mais do que uma
vez em paralelo!
0..1
0..1
1
*
27. public class Floor {
private LinkedList<Room> rooms =
new LinkedList<Room>();
public Floor(final int numberOfRooms) {
for (int roomNumber = 1;
roomNumber != numberOfRooms + 1;
roomNumber++)
rooms.add(new Room(roomNumber));
}
public void show() {
while (rooms.hasNext())
out.println(rooms.next());
}
}
2013/2014 Fundamentos de Programação 27
28. public class Floor {
private LinkedList<Room> rooms =
new LinkedList<Room>();
public Floor(final int numberOfRooms) {
for (int roomNumber = 1;
roomNumber != numberOfRooms + 1;
roomNumber++)
rooms.add(new Room(roomNumber));
}
public void show() {
LinkedList<Room>.Iterator i = rooms.iterator();
while(i.hasNext())
out.println(i.next());
}
}
2013/2014 Fundamentos de Programação 28
29. public class Floor {
private LinkedList<Room> rooms =
new LinkedList<Room>();
public Floor(final int numberOfRooms) {
for (int roomNumber = 1;
roomNumber != numberOfRooms + 1;
roomNumber++)
rooms.add(new Room(roomNumber));
}
public void show() {
Iterator<Room> i = rooms.iterator();
while(i.hasNext())
out.println(i.next());
}
}
2013/2014 Fundamentos de Programação 29
30. Iterator é uma
classe interna (inner).
As suas instâncias têm
uma ligação implícita
à instância da classe
envolvente que as
criou. A ver com
pormenor mais tarde.
2013/2014 Fundamentos de Programação 30
LinkedList
Item
Node
Item
- item: Item
nextNode
firstNode currentNode
0..10..1
0..1
Esta solução permite
percorrer a lista mais
do que uma vez em
paralelo!
Iterator
LinkedList.this
*
0..1
0..1
*
*
1
31. Permitem iterar ao longo de uma sequência de
elementos, actuando sobre essa sequência
A uma sequência podem associar-se vários
iteradores
Classes que forneçam iteradores podem ser
usadas em ciclos for-each…
… mas têm de implementar a interface Iterable
2013/2014 Fundamentos de Programação 31
32. public class Floor {
private LinkedList<Room> rooms =
new LinkedList<Room>();
public Floor(final int numberOfRooms) {
for (int roomNumber = 1;
roomNumber != numberOfRooms + 1;
roomNumber++)
rooms.add(new Room(roomNumber));
}
public void show() {
for (Room room : rooms)
out.println(room);
}
}
2013/2014 Fundamentos de Programação 32
Mais tarde voltaremos a este assunto.
33. Interfaces
Definem comportamentos (adjectivam)
Classes implementam-nas
Listas
Listas ligadas
Operações sobre listas ligadas
Introdução aos iteradores
2013/2014 Fundamentos de Programação
Página
33
34. Interfaces
Listas e listas ligadas
Iteradores
2013/2014 Fundamentos de Programação
Página
34
Notas del editor
Apesar de terem pontos em comum, as interfaces diferem conceptualmente e na prática das classes abstractas.
Note-se que aqui introduzimos dois conceitos em simultâneo: o de interface e o de definição de um tipo genérico. No entanto, as interfaces podem não ser genéricas!
Note-se que quando nos limitamos a declarar uma operação (e.g., uma operação numa interface ou uma operação abstracta numa classe), não há qualquer utilidade em qualificar os seus parâmetros como final. De facto, nos parâmetros, o qualificador final é uma protecção adicional para o programador enquanto implementador da operação!
Comparable é semelhante a java.lang.Comparable.
Queue é semelhante a java.util.Queue.
Note-se que a classe Rational declara implementar a interface Comparable parametrizada consigo mesma!
No final podia usar-se simplesmente return leftNumerator - rightNumerator.
Esta implementação não tenta evitar a ultrapassagem dos limites dos int!
A relação entre uma interface e uma sua implementação é de realização. Uma implementação realiza uma interface.
As interfaces, tal como as classes, são tipos. O polimorfismo não se aplica apenas entre classes, mas entre tipos em geral. Uma instância de um subtipo pode (deve poder!) ser tratada como se fosse do respectivo tipo.
A segunda possibilidade não é isenta de problemas. Temos os patos e temos também aquilo que “walks like a duck and swims like a duck and quacks like a duck” mas não é um pato. Limita-se a comportar-se como tal. Damos o nome Duck à classe ou à interface e isso gera confusão ou mesmo conflito. Talvez a segunda devesse ser Duckish ou DuckLike…
O que é constante e o que varia? Esta é a base da identificação de unidades de modularização parametrizáveis. A genericidade em Java é uma forma de polimorfismo paramétrico!
Solução péssima do Java original. Os elementos tinham de ser tratados como objectos (que têm disponível um comportamento limitado). Alternativamente, podia usar-se coerções de tipo, mas que tinham a particularidade infeliz de poder falhar em tempo de execução!
Agora sim!
A implementação (e discussão!) dos médodos add, hasNext e next fica como exercício para o leitor.
A ideia é que os itens são guardados em nós possuídos pela lista e que se encadeiam uns nos outros, ficando a lista com uma ligação para o primeiro.
Em rigor, apenas a notação inferior esquerda existe no UML. As restantes notações existem no UML mas apenas no que diz respeito a pacotes. Abusa-se aqui um pouco, por isso, da notação oficial.
A implementação (e discussão!) do médodo hasNext fica como exercício para o leitor. Os restantes são desenvolvidos mais à frente. Na realidade faz mais sentido inserir os novos quartos no final da lista.
Na realidade deveria escrever-se LinkedList<E → Room>::Node<E → Room>, mas é muito longo para um diapositivo!
Código já visto. Ver em sequência com o próximo diapositivo.
Note-se que a classe Iterator está definida dentro da classe LinkedList<Room>. É necessário um método adicional na lista: iterator().
A classe LinkedList<Room>.Iterator implementará a interface java.util.Iterator<Room>.
Este diapositivo é animado. Conceptualmente os nós são parte da lista. Mas a navegação faz-se usando uma ligação para o primeiro nó e de cada nó para o seguinte.
A classe LinkedList<Room>.Iterator implementará a interface java.util.Iterator<Room>.