Buscar

classes-abstratas

Prévia do material em texto

Ken Arnold, ex-engenheiro sênior do Sun Microsystems Laboratories, é um grande especialista em
projeto e implementação orientada a objetos. Ele foi um dos arquitetos originais da tecnologia
Jini™ e engenheiro chefe da tecnologia JavaSpaces™ da Sun.
James Gosling é um Sun Fellow e CTO do Grupo de Desenvolvimento da Plataforma da Sun
Microsystems. Projetou a linguagem de programação Java original, implementou seu compilador
e a máquina virtual originais, e, mais recentemente, contribuiu para a Real-Time Specification de
Java. Um dos mais respeitados programadores da indústria, ele recebeu o prêmio de Excelência em
Programação da Software Development do ano de 1996.
David Holmes é diretor da DLTech Pty Ltd, localizada em Brisbane, Austrália. Especialista em
sincronização e concorrência, participou do grupo de especialistas JSR-166 que desenvolveu os novos
utilitários de concorrência. Ele também contribuiu para a atualização da Real-Time Specification de
Java e passou os últimos anos trabalhando em uma implementação desta especificação.
A756l Arnold, Ken
A linguagem de programação Java [recurso eletrônico] / Ken Arnold, 
Jamoes Gosling, David Holmes ; tradução Maria Lúcia Blank Lisbôa. – 4. ed. – 
Dados eletrônicos. – Porto Alegre : Bookman, 2007.
Editado também como livro impresso em 2007.
ISBN 978-85-60031-61-0
1. Computação – Linguagem de programação. I. Gosling, James. 
II. Holmes, David. III. Título.
CDU 004.438JAVA
Catalogação na publicação: Juliana Lagôas Coelho – CRB 10/1798
Catalogação na publicação: Júlia Angst Coelho – CRB 10/1712
3.7 Classes e Métodos Abstratos
Uma característica externamente útil da programação orientada a objetos é o conceito de
classe abstrata. Usando classes abstratas você pode declarar classes que definem somen-
te parte de uma implementação, deixando para as classes estendidas fornecer implemen-
tações específicas de alguns ou todos os métodos. O oposto de abstrato é concreto – uma
classe que possui somente métodos concretos, incluindo implementações de quaisquer
métodos abstratos herdados de superclasses, é uma classe concreta.
Classes abstratas auxiliam quando algum comportamento é definido para a maioria ou todos
os objetos de um dado tipo, mas outros comportamentos fazem sentido somente para classes
particulares e não para uma superclasse geral.Uma classe como essa é declarada abstract,
e cada método não implementado na classe também é marcado como abstract. (Se você
necessita definir alguns métodos mas você não precisa providenciar qualquer implementação,
você provavelmente vai querer usar interfaces, que são descritas no Capítulo 4.)
Por exemplo, suponha que você quer criar uma bateria de testes de medidas de desem-
penho para fornecer uma infra-estrutura para escrever código para benchmarks. A im-
plementação da classe deve entender como dirigir e medir o desempenho, mas ela pode
não saber antecipadamente qual benchmark será executado. A maioria das classes abs-
tratas se enquadra num padrão no qual uma área de conhecimento particular da classe
exige que outra pessoa providencie uma parte que está faltando – isto é comumente co-
nhecido como padrão “Método Gabarito”. Em muitos casos os métodos especialistas
são bons candidatos a serem final, de modo que o conhecimento não possa ser com-
prometido de algum modo. Neste exemplo de medidas de desempenho, a parte que está
faltando é o código que precisa ser avaliado. Aqui está como uma classe como esta po-
deria se parecer:
abstract class Benchmark {
abstract void benchmark();
public final long repeat(int count) {
long start = System.nanoTime();
for (int i = 0; i < count; i++)
benchmark();
return (System.nanoTime() – start);
}
}
Qualquer classe com métodos abstratos precisa ser declarada abstract. Esta redundân-
cia auxilia o leitor a ver rapidamente que esta classe é abstract, sem ter que inspecio-
nar a classe para ver se algum método é declarado abstract.
O método repeat fornece a especialidade em benchmark. Ele pode medir o tempo de
uma execução de count repetições de benchmark. O método System.nanoTime retor-
na um timestamp em nanosegundos – ver páginas 594-595. Pela subtração do horário ini-
cial do horário final, você obtém uma aproximação do tempo despendido executando o
benchmark. Se a medição de tempo necessita se tornar mais complexa (talvez medir o
tempo de cada execução e calcular estatísticas sobre as variações), este método pode ser
melhorado sem afetar nenhuma implementação em qualquer classe estendida de seu có-
digo especializado de benchmark.
CAPÍTULO 3 • ESTENDENDO CLASSES 111
O método abstract benchmark deve ser implementado em cada subclasse que não se-
ja também abstract. Isto se deve ao fato de não haver implementação nesta classe, ape-
nas uma declaração. Aqui está um exemplo de uma extensão de Benchmark simples:
class MethodBenchmark extends Benchmark {
/** Nada faz, apenas retorna. */
void benchmark() {
}
public static void main(String[] args) {
int count = Integer.parseInt(args[0]);
long time = new MethodBenchmark().repeat(count);
System.out.println(count + " métodos em " +
time + " nanosegundos");
}
}
Esta classe mede o tempo necessário para invocar um método vazio benchmark, mais a
sobrecarga do laço. Você agora pode medir invocações de métodos executando a aplica-
ção MethodBenchmark com o número de vezes a ser repetido o teste. O valor de count
é obtido a partir dos argumentos do programa e decodificado usando o método par-
seInt da classe Integer sobre o argumento string, como descrito em “Conversões de
Strings” na página 294.
Qualquer classe pode sobrescrever métodos de sua superclasse para declará-los abs-
tract, convertendo um método concreto em um abstrato naquele ponto da árvore de ti-
pos. Esta técnica é útil, por exemplo, quando a implementação default da classe é inváli-
da para uma parte da hierarquia de classes.
Você não pode criar um objeto de uma classe abstract porque poderia não existir uma
implementação válida para alguns métodos que bem poderiam ser invocados.
Exercício 3.5: Escreva uma nova classe estendida que faça mais medidas, tal como
quanto tempo demora a execução de um laço de zero até algum parâmetro passado.
Exercício 3.6: Altere Veiculo para que ela tenha uma referência a um objeto FonteE-
nergia que é associada com Veiculo em seu construtor. FonteEnergia deve ser uma
classe abstract, porque a medida de capacidade de um objeto TanqueGasolina dife-
re da medida de um objeto Bateria. Coloque um método abstract vazio em Fon-
teEnergia e implemente-o nas classes TanqueGasolina e Bateria. Adicione um
método start a Veiculo que assegure que a fonte de energia não está vazia.
3.8 A Classe Object
A classe Object é a raiz da hierarquia de classes. Cada classe, direta ou indiretamente,
estende Object e portanto uma variável do tipo Object pode se referir a qualquer obje-
to, seja uma instância de classe ou um array. Por exemplo, a classe Attr pode guardar um
atributo de qualquer tipo, e assim seu campo value foi declarado como sendo do tipo
Object. Uma classe como essa não pode armazenar tipos primitivos diretamente, mas
pode conter referências de classes invólucro associadas – ver Capítulo 8.
112 A LINGUAGEM DE PROGRAMAÇÃO JAVA
A classe Object define vários métodos que são herdados por todos os objetos. Estes mé-
todos pertencem a duas categorias: métodos utilitários gerais e métodos que suportam
threads. Suporte a threads é abordado no Capítulo 14. Esta seção descreve os métodos
utilitários e como eles afetam classes. Os métodos utilitários são:
public boolean equals(Object obj)
Compara o objeto receptor com o objeto referido por obj para verificar a igualda-
de, retornando true se eles possuem o mesmo valor e falso se eles não possuem.
Se você quer determinar se duas referências se referem ao mesmo objeto, você po-
de compará-las usando == e !=. O método equals se preocupa com igualdade de
valor. A implementação default de equals em Object assume que um objeto é
igual somente a si mesmo, testando se this == obj.
public inthashCode()
Retorna um código hash para este objeto. Cada objeto possui um código hash para
uso em tabelas hash. A implementação default retorna um valor que é geralmente
diferente para diferentes objetos. Ele é usado para armazenar objetos em coleções
hash, como descrito no Capítulo 21.
protected Object clone() throws CloneNotSupportedException
Retorna um clone deste objeto. Um clone é um novo objeto que é uma cópia do ob-
jeto sobre o qual clone é invocado. Clonagem é discutida na próxima seção.
public final Class<?> getClass()
Retorna um token de tipo que representa a classe deste objeto. Para cada classe T
existe um token de tipo Class<T> (lido como “classe de T”) que é uma instância
da classe genérica Class, descrita em “A Classe Class” nas páginas 365-366.
Quando invocado sobre uma instância de Object, este método vai retornar uma
instância de Class<Object>; quando invocado sobre uma instância de Attr irá
retornar uma instância de Class<Attr>, e assim por diante.
protected void finalize() throws Throwable
Finaliza o objeto durante a coleta de lixo. Este método é discutido com detalhes em
“Finalização”, nas páginas 407-408.
public String toString()
Retorna uma representação string do objeto. O método toString é implicitamen-
te invocado sempre que uma referência de objeto é usada dentro de uma expressão
de concatenação de string como um operando do operador +. A versão Object de
toString constrói um string contendo o nome da classe do objeto, um caractere
@ e uma representação hexadecimal do código hash da instância.
Ambos os métodos, hashCode e equals podem ser sobrescritos se você quiser fornecer
uma noção de igualdade diferente da implementação default fornecida pela classe Ob-
ject. O default é que quaisquer dois objetos diferentes não são iguais e seus códigos
hash são usualmente distintos.
Se sua classe possui a noção de igualdade na qual dois objetos diferentes podem ser
iguais, esses dois objetos devem retornar o mesmo valor de hashCode. O mecanismo
usado por coleções hash se apóia em equals retornando true quando encontra uma
chave de mesmo valor na tabela. Por exemplo, a classe Strings sobrescreve equals pa-
CAPÍTULO 3 • ESTENDENDO CLASSES 113
ra retornar true se dois objetos String possuem o mesmo conteúdo. Ela também so-
brescreve hashCode para retornar um hash baseado no conteúdo do String, de modo
que dois strings com o mesmo conteúdo possuam o mesmo hashCode.
O termo identidade é usado para igualdade de referência: se duas referências são idênticas,
então == entre as duas será true. O termo equivalência descreve igualdade de valor – obje-
tos que podem ou não ser idênticos, mas para os quais equals vai retornar true. Assim se
pode dizer que a implementação default de equals é que equivalência é o mesmo que iden-
tidade. Uma classe que define uma noção mais ampla de igualdade pode fazer que objetos
que não são idênticos sejam equivalentes, sobrescrevendo equals para retornar true com
base no estado dos objetos, em vez de suas identidades. Algumas tabelas hash, tais como ja-
va.util.IdentityHashMap, são preocupadas com a identidade de objetos, e não a equi-
valência. Se você precisa escrever uma tabela hash como essa, você quer códigos hash cor-
respondentes à identidade de objetos, e não a seus estados. O método System.identity-
HashCode retorna o mesmo valor que a implementação de hashCode da classe Object po-
deria retornar para um objeto se não for sobrescrito. Se você simplesmente usa hashCode
sobre os objetos que você está armazenando, você poderia ter um código hash baseado em
equivalência, não em identidade, o que poderia ser muito menos eficiente.
Exercício 3.7: Sobrescreva equals e hashCode para ColorAttr.
3.9 Clonar Objetos
O método Object.clone ajuda você a escrever métodos clone para sua próprias classes.
Um método clone retorna um novo objeto cujo estado inicial é uma cópia do estado atual
do objeto sobre o qual clone foi invocado. Alterações subseqüentes no novo objeto clo-
nado não devem afetar o estado do objeto original.
3.9.1 Estratégias para Clonagem
Existem três importantes fatores na escrita de um método clone:
• A interface Cloneable vazia, a qual você deve implementar para providenciar
um método clone que pode ser usado para clonar um objeto3.
• O método clone implementado pela classe Object, que faz um clone simples
através da cópia de todos os campos do objeto original para o novo objeto. Este
método funciona para muitas classes, mas pode haver necessidade de ser suple-
mentado por um método que o sobrescreva.
• A CloneNotSupportedException, que pode ser usada para sinalizar que um
método clone de uma classe não deveria ter sido invocado.
Uma dada classe pode ter uma, dentre as quatro atitudes seguintes, perante clonagem:
• Suportar clone. Tal classe implementa Cloneable e declara seu método clone
para não lançar exceções.
114 A LINGUAGEM DE PROGRAMAÇÃO JAVA
3
Cloneable deveria ter sido escrita como Clonable, mas o engano foi constatado tarde demais para ser corrigido.

Continue navegando