7
Abstração Genérica
 Unidades genéricas e instanciação.
 Tipos e classes parâmetros.
 Notas de implementação.
7-1
Unidades genéricas e instanciação (1)
 Uma unidade genérica é uma unidade de programa que é
parametrizada em relação às entidades das quais ela
depende
 A instanciação de uma unidade genérica cria uma unidade
de programa ordinária, na qual, cada parâmetro formal da
unidade é trocado por um argumento
• Gera sob demanda uma família de unidades de programas similares,
evitando a redundância de código
• Favorece a reusabilidade porque uma mesma unidade de programa
pode ser instanciada em diferentes programas
7-2
Unidades genéricas e instanciação (2)
 Unidades genéricas seguem o princípio da abstração
• Uma unidade genérica é uma abstração sobre uma declaração
– Possui um corpo – a declaração – e uma instanciação genérica é uma
declaração que produzirá ligações pela elaboração do corpo da
unidade genérica
 Unidades genéricas são suportadas por Ada, C++ e Java
em diferentes perspectivas
7-3
Exemplo: pacotes genéricos em Ada (1)
 Ada suporta pacotes genéricos e procedimentos genéricos

generic
capacity: Positive;
package Queues is
Parâmetro formal
baseado em valor
type Queue is limited private;
procedure clear (q: out Queue);
procedure add (q: in out Queue; e: in Character);
procedure remove (q: in out Queue; e: out Character);
private
type Queue is
record
length: Integer range 0 .. capacity;
front, rear: Integer range 0 .. capacity-1;
elems: array (0 .. capacity-1) of Character;
end record;
end Queues;
7-4
Exemplo: pacotes genéricos em Ada (2)

package body Queues is
procedure clear (q: out Queue) is begin
q.front := 0; q.rear := 0; q.length := 0;
end;
procedure add (q: in out Queue; e: in Character) is begin
q.elems(q.rear) := e;
q.rear := (q.rear + 1) mod capacity;
q.length := q.length + 1;
end;
procedure remove (q: in out Queue; e: out Character) is begin
e := q.elems(q.front);
q.front := (q.front + 1) mod capacity;
q.length := q.length - 1;
end;
end Queues;
7-5
Exemplo: pacotes genéricos em Ada (3)
 Pacotes genéricos em Ada são instanciados por uma forma
especial de declaração denominada instantiation
•
•
package Line_Buffers is new Queues(120);
package Input_Buffers is new Queues(80);
Argumentos da
instanciação
 A instanciação resulta num pacote ordinário
•
inbuf: Input_Buffers.Queue;
...
Input_Buffers.add(inbuf, '*');
7-6
Exemplo: pacotes genéricos em C++ (1)
 C++ suporta funções genéricas (function templates) e
classes genéricas (class templates)

template
<int capacity>
class Queue {
Parâmetro formal da classe genérica – denota um
valor inteiro a ser conhecido durante a instanciação
private:
char elems[capacity];
int front, rear, length;
public:
Queue ();
void add (char e);
char remove ();
}
7-7
Exemplo: pacotes genéricos em C++ (2)
 Definição de construtores e métodos em separado

template
<int capacity>
Queue<capacity>::Queue () {
front = rear = length = 0;
}
template
<int capacity>
void Queue<capacity>::add (char e) {
elems[rear] = e;
rear = (rear + 1) % capacity;
length++;
}
template
<int capacity>
char Queue<capacity>::remove () {
char e = elems[front];
front = (front + 1) % capacity;
length--;
return e;
}
7-8
Exemplo: pacotes genéricos em C++ (3)
 Instanciação de pacotes genéricos
•
typedef Queue<80> Input_Buffer;
typedef Queue<120> Line_Buffer;
 A instanciação resulta em classes nas quais os parâmetros
formais são substituídos pelo valor do argumento
•
Input_Buffer inbuf;
Line_Buffer outbuf;
Line_Buffer errbuf;
• ou
•
Queue<80> inbuf;
Queue<120> outbuf;
Queue<120> errbuf;
7-9
Exemplo: pacotes genéricos em C++ (4)
 A instanciação on the fly de classes genéricas resulta num
problema conceitual e em outro de ordem pragmática
• O problema conceitual diz respeito a equivalência de tipos
– Duas variáveis outbuf e errbuf declaradas com tipos
Queue<120> e Queue<120> são equivalentes
– Mas se duas variáveis são declaradas com tipos Queue<m> e
Queue<n-1>, o compilador não tem como decidir se os tipos são
equivalentes
Estes problemas não ocorrem em Ada!
–  instanciações em C++ devem obrigatoriamente poder avaliar seus
argumentos em tempo de compilação
• O problema pragmático pode levar o programador a perder o
controle sobre a expansão de código
– Se Queue<120> ocorre em vários locais do código, um compilador
não profissional pode gerar várias instâncias de Queue, enquanto um
7-10
profissional geraria apenas uma única instância
Tipos e classes parâmetros
 Como uma unidade de programa usa um valor definido em
qualquer lugar, a unidade de programa pode tornar-se
genérica e parametrizada com relação a esse valor
• Graças ao princípio da correspondência
 Como uma unidade de programa usa um tipo (ou classe)
definido em qualquer lugar, a unidade de programa pode
tornar-se genérica e parametrizada com relação a esse tipo
• Tem-se uma nova modalidade de parâmetro: o parâmetro que é um
tipo (ou uma classe)
 Unidade genéricas em Ada, C++ e Java podem ter tipos
como parâmetros
7-11
Exemplo: tipo parâmetro em Ada (1)
 Uma unidade genérica em Ada pode ser parametrizada
com respeito a qualquer tipo do qual ela dependa

Parâmetro formal do pacote genérico
generic
type Element is private;
– denota um tipo a ser conhecido
package Lists is
durante a instanciação
type List is limited private;
procedure clear (l: out List);
procedure add (l: in out List; e: in Element);
...
private
capacity: constant Integer := ...;
type List is
record
length: Integer range 0 .. capacity;
elems: array (1 .. capacity) of Element;
end record;
end Lists;
7-12
Exemplo: tipo parâmetro em Ada (2)
 O parâmetro formal Element é usado tanto da especificação
do pacote, quanto no seu corpo

package body Lists is
procedure clear (l: out List) is begin
l.length := 0;
end;
procedure add (l: in out List; e: in Element) is begin
l.length := l.length + 1;
l.elems(l.length) := e;
end;
...
end Lists;
7-13
Exemplo: tipo parâmetro em Ada (3)
 Instanciação do pacote genérico Lists
– package Phrases is new Lists(Character);
• O parâmetro formal Element é ligado ao tipo Character
(argumento na instanciação)
• Depois, a especificação e o corpo de Lists são elaborados,
gerando um pacote que encapsula uma lista com elementos do tipo
Character
– sentence: Phrases.List;
...
Phrases.add(sentence, '.');
• Outra opção
– type Transaction is record ... end record;
...
package Transaction_Lists is new Lists(Transaction);
7-14
Exemplo: tipo e função parâmetro
em Ada (1)
 Se uma unidade genérica não sabe nada sobre o tipo
parâmetro – exceto o nome – ela ainda assim pode declarar
variáveis desse tipo, mas não pode aplicar nenhuma
operação sobre tais variáveis!
•  Unidades genéricas devem saber pelo menos algumas das
operações aplicáveis ao tipo parâmetro
– Tipicamente, as operações de atribuição e de teste de igualdade
7-15
Exemplo: tipo e função parâmetro
em Ada (2)

generic
type Element is private;
with function precedes (x, y: Element) return Boolean;
package Sequences is
Parâmetro formal que
type Sequence is limited private;
procedure clear (s: out Sequence);
denota a uma
função desconhecida que recebe dois
argumentos do tipo Element e retorna
um resultado do tipo Boolean
procedure append (s: in out Sequence; e: in Element);
procedure sort (s: in out Sequence);
private
capacity: constant Integer := ...;
type Sequence is record
length: Integer range 0 .. capacity;
elems: array (1 .. capacity) of Element;
end record;
end Sequences;
7-16
Exemplo: tipo e função parâmetro
em Ada (3)
 O corpo do pacote usa o tipo Element e a função precedes,
parâmetros da unidade genérica

package body Sequences is
...
procedure sort (s: in out Sequence) is
e: Element;
begin
...
if precedes(e, s.elems(i)) then ...
...
end;
end Sequences;
7-17
Exemplo: tipo e função parâmetro
em Ada (4)
 Possível instanciação de Sequences

type Transaction is record ... end record;
function earlier (t1, t2: Transaction) return Boolean;
-- Return true if and only if t1 has an earlier timestamp than t2.
package Transaction_Sequences is
new Sequences(Transaction, earlier);
 que pode ser usada como um pacote comum

audit_trail: Transaction_Sequences.Sequence;
...
Transaction_Sequences.sort(audit_trail);
 Outros exemplos

package Ascending_Sequences is new Sequences(Float, "<");
readings: Ascending_Sequences.Sequence; ...
Ascending_Sequences.sort(readings);
7-18
Exemplo: tipo e função parâmetro
em Ada (5)
 Em geral, se uma unidade genérica em Ada tem um tipo
parâmetro T, ele é especificado pela cláusula
•
type T is especificação das operações que equipam T;
 O compilador verifica a unidade genérica para assegurar que
• operações usadas por T na unidade genérica  nas operações com as
quais T está equipada
 O compilador verifica separadamente cada instanciação da
unidade genérica para assegurar que
• operações com as quais T está equipada  nas operações que
equipam o tipo que é passado como argumento
 Uma vez implementada, as unidades genéricas em Ada
podem ser reusada com segurança em relação a verificação
7-19
de tipos
Exemplo: tipo parâmetro em C++ (1)
 Unidades genéricas em C++ podem ser parametrizadas em
relação a quaisquer tipos ou classes das quais elas dependam

template
<class Element>
class Sequence is
private:
const int capacity = ...;
int length;
Element elems[capacity];
Parâmetro formal que denota a um
tipo qualquer desconhecido, e não
apenas tipos que são classes
public:
Sequence ();
void append (Element e);
void sort ();
}
7-20
Exemplo: tipo parâmetro em C++ (2)
 Implementação segue como usual

template
<class Element>
void Sequence<Element>::sort () {
Element e;
...
if (e < elems[i]) ...
...
}
• A utilização do operador "<" é perfeitamente legal, pois assume-se
que o tipo denotado por Element "estará" equipado com tal operador
– O compilador rejeitará instanciações para tipos argumentos que não
sejam equipados com o operador "<"
7-21
Exemplo: tipo parâmetro em C++ (3)
 Exemplos de instanciações

typedef Sequence<float> Number_Sequence;
Number_Sequence readings;
...
readings.sort();
O operador "<" será utilizado, mas o
resultado não será o desejado! Por que?

typedef char* String;
typedef Sequence<String> String_Sequence;

struct Transaction { ... };
typedef Sequence<Transaction> Transaction_Sequence;
Esta instanciação será recusada pelo
compilador, pois o operador "<" não
equipa o tipo Transaction
7-22
Exemplo: tipo parâmetro em C++ (4)
 Em geral, se uma unidade genérica em C++ tem um tipo
parâmetro T, ele é especificado pela cláusula
• <class T>
 que não revela nada sobre as operações que equipam T. O
compilador verifica cada instanciação da unidade genérica
para assegurar que
• operações usadas por T na unidade genérica  nas operações que
equipam o tipo que é passado como argumento
 As unidades genéricas de C++ não são uma fundação segura
para reuso de software
• O compilador não é capaz de verificar completamente os tipos numa
definição de unidade genérica, mas apenas instanciações individuais
7-23
Exemplo: classe genérica em Java com classe
parâmetro (1)
 Unidades genéricas foram introduzidas em Java com o
lançamento da plataforma Java 2SE 5.0 em 2004
• Classes genéricas – classes que podem ser parametrizadas em relação
a outras classes

class List <Element> {
private int length;
private Element[] elems;
public List () {
...
}
Parâmertro formal da classe genérica
List e denota uma classe desconhecida
a priori
public void append (Element e) {
...
}
...
}
7-24
Exemplo: classe genérica em Java com classe
parâmetro (2)
 Instanciação de classes genéricas

List<Character> sentence;
List<Transaction> transactions;
 O argumento na instanciação deve ser uma classe – tipos
primitivos são proibidos

List<char> sentence;
Instanciação ilegal!
7-25
Exemplo: classe genérica em Java com classe
parâmetro limitada pela interface (1)
 Caso uma classe genérica assuma que a classe parâmetro
está equipada com operações, a classe parâmetro deve ser
especificada como implementando uma interface adequada
• Diz-se que tal classe parâmetro é limitada (bounded) pela interface

class Sequence <Element implements Comparable<Element>> {
private int length;
private Element[] elems;
public Sequence () { ... }
public void append (Element e) { ... }
public void sort () {
Element e;
...
if (e.compareTo(elems[i] < 0) ...
...
}
}
Element é um parâmetro
formal que denota uma classe
desconhecida, mas que deve
implementar
a
interface
Comparable,
onde
o
método compareTo está
7-26
especificado
Exemplo: classe genérica em Java com classe
parâmetro limitada pela interface (2)
 Possível instanciação da classe genérica Sequence

Sequence<Transaction> auditTrail;
...
auditTrail.sort();
 assumindo que a classe Transaction é declarada assim

Class Transaction implements Comparable<Transaction> {
private ...;
public int compareTo(Transaction that) {
...
}
...
}
7-27
Exemplo: classe genérica em Java com classe
parâmetro limitada pela interface (3)
 Em geral, se uma unidade genérica em Java tem uma classe
Desvantagens
da classes
genéricas
emcláusula
Java
parâmetro C, ela
é especificada
pela
• C implements Interface;
• Somente classes podem ser parâmetros de classes
 O compilador verifica a unidade genérica para assegurar que
genéricasusadas por C na unidade genérica  nas operações
• operações
declaradas em Interface
• Tipos primitivos
são proibidos – conseqüência da incompletude
 O compilador
verifica separadamente
cada instanciação da
unidadedegenérica
para assegurar que
tipos em Java
• operações declaradas na Interface  nas operações que equipam a
classe passada como argumento
• Não podem ser parametrizadas por valores dos quais
 Uma vez implementada, as unidades genéricas em Java
dependam
podem
ser reusadas com segurança em relação a verificação
7-28
de tipos
Notas de Implementação
 Unidades genéricas com tipos parâmetros suscitam
interessantes problemas de implementação
• Como o tipo parâmetro denota um tipo desconhecido, o
compilador não pode determinar, apenas da unidade genérica,
como os valores do tipo serão representados
– Não há como saber o espaço de memória requerido, por exemplo
• Somente quando a unidade genérica é instanciada é que o
compilador pode saber essa informação
7-29
Exemplo: implementação de unidades
genéricas em Ada (1)
7-30
Exemplo: implementação de unidades
genéricas em Ada (2)
 As três instanciações anteriores definem três tipos distintos
• A.List
• B.List
• C.List
 Em geral, cada instanciação de um pacote genérico de Ada
é compilado, com a geração de código objeto especializado
para cada instanciação
• Em casos específicos, diferentes instanciações podem compartilhar
o mesmo código objeto – quando os tipos envolvido têm
representações similares
7-31
Exemplo: implementação de unidades
genéricas em C++
 Similar a implementação em Ada, mas com uma
complicação extra
• As classes genéricas de C++ podem ser instanciadas on the fly
– Um código de programa pode conter várias declarações de variáveis
do tipo
 List<float>
– No sistema de tipos de C++, todas estas variáveis têm o mesmo tipo,
logo estão equipadas com as mesmas operações
– O desafio para o compilador é fazer com que todas estas variáveis
compartilhem o mesmo código objeto
7-32
Exemplo: implementação de unidades
genéricas em Java (1)
 A implementação de classes genéricas em Java é
simplificada porque elas só podem ser parametrizadas em
relação a classes, que sempre são acessadas através de
apontadores
7-33
Download

7. Abstrações genéricas