Baixe o app para aproveitar ainda mais
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.
Compartilhar