Este texto foi atualizado e doado ao projeto Code Design World. |
Comparações em Java
Para quem já usou uma linguagem de programação antes do Java, quando começa a usar Java tem uma certa dificuldade em fazer comparações de objetos. Principalmente testar a igualdade.
Java é fortemente orientado a objetos e isso tem profundas implicações. Uma delas passa pela diferenciação entre variável primitiva e variável de referência. Basicamente, uma variável primitiva expressa o próprio valor da variável enquanto uma variável de referencia é apenas um apontador para um objeto. Como existem variáveis de referencia para objetos e os objetos em si, a linguagem tem que oferecer formas de testar a igualdade das váriaveis, por um lado, e dos objetos, por outro. O teste de igualdade de variáveis é feito pelo operador == que compara o valor da variável. O teste de igualdade de objetos é feito pelo uso do método equals() presente em todos os objetos.
|
|
Código 1: |
O facto de equals() ser um método presente em qualquer objeto permite que a lógica que compara o objeto seja modificada conforme o tipo de objeto. Este facto é importantissimo porque podemos ter controlo sobre o tipo de comparação que estamos fazendo mesmo quando o objeto não foi codificado por nós. Esta forma de comparação de objetos é principalmente importante quando agrupamos os objetos em coleções.
O método equals() presente em todos os objetos faz as vezes do operador de comparação de valor presente em outras linguagens (= ou ==, por exemplo). Contudo, testar a igualdade não é a única comparação que queremos fazer entre objetos.
Ordem: Maiores e Menores
Alguns objetos têm uma ordem intrínseca para os seus valores. O exemplo clássico são os objetos que representam números, mas também datas e sequencias de caracteres têm uma ordem intrínseca.
Para este tipos de objetos é comum termos que saber se um certo objeto tem valor menor ou maior que outro Existem duas formas em Java de comparar a ordem dos objetos: implementar Comparable ou implementar Comparator
Implementar Comparator
Em outras linguagens e em java quando trabalhamos com tipos primitivos podemos usar operador de comparação de ordem como > e < ou mesmo >= e <=.
|
|
Código 2: |
Podemos pensar em outra forma de comparar os valores de a e b usando a operação de subtração.
|
|
Código 3: |
Uma simples alteração matemática da forma de comparar e passámos a comparar um numero com zero. Agora basta aplica o principio de separação de responsabilidade e encapsular a operação num método
|
|
Código 4: |
Poderiamos agora trocar a definição de a e b para objetos que a nossa forma de comparação não se altera.
|
|
Código 5: |
Basta agora padronizar o uso daquele método criando uma interface, que qualquer objeto pode implementar e onde esconder a real comparação dos objetos. Essa interface é: Comparator
|
|
Código 6: |
Comparator permite construir qualquer forma de comparação de dois objetos. É especialmente indicado quando o objeto não tem uma ordem intrínseca ou tem mais do que uma ordem possível. Por exemplo: Produto pode ser ordenado por nome, for fabricante ou por preço.
A implementação de compare() é baseada no conceito de que estamos subtraindo os valores dos objetos como se eles fossem numeros. Então, se os objetos representam o mesmo valor, o resultado será zero. Se o valor representado por a for maior que o de b o resultado é um numero maior que zero. Qualquer número, maior que zero, pode ser retornado. Se o valor representado por a for menor que o de b o resultado é um numero menor que zero. Qualquer número, menor que zero, pode ser retornado.
Com estas regras simples, é possivel implementar comparação para qualquer tipo de objeto. Afinal matemática é muito mais que fazer contas…
Implementar Comparable
Algums objetos, como aqueles que representam numeros e datas, por exemplo, têm uma ordem intrinseca – chamada de ordem natural. Para os objetos que têm uma ordem natural o objeto, ele próprio, deve implementar a interface Comparable. A implementação do único método de Comparable, compareTo() segue as mesmas regras que o método em Comparator com a vantagem de não obrigar a instanciar um objeto especial para fazer a comparação.
compareTo() vs. equals()
Ao implementar Comparable temos agora duas formas de saber se dois objetos são iguais:
|
|
Código 7: |
Será que podemos usar qualquer um dos métodos indescriminadamente ? Não. Estes dois métodos só são equivalentes se a implementação de compareTo() for compativel com equals(). Ou seja, se quando os objetos são iguais, a diferença entre eles é zero. É necessário consultar a documentação da implementação do objeto para saber se a implementação é,ou não, compativel. Quando o uso é equivalente diz-se que “a implementação de Comparable é compatível com equals”.
Um exemplo de objeto cujo método Compare() pode não ser compativel com equals() é Money . O objecto Money contém um valor numérico e uma moeda. Dois valores de money são iguais apenas quando o valor numérico e a moeda forem iguais. Contudo, os valores de money podem ser considerados maiores ou menores apenas com base no valor numérico.
Nota: Esta implementação de Money, embora possivel e um exemplo de um objeto comparável não compativel com equals , não é uma implementação desejável. Objetos Money de moedas diferentes não devem sequer ser comparáveis. Uma leitura do Javadoc de Comparable é aconcelhavel
Conclusão
Em Java, o operador == testa a igualdade do valor da variável e não o valor do conteúdo da variável. A diferença só é relevante quando a variável é uma referência a um objeto.
Para descobrir se dois objetos são iguais temos que usar o método equals() presente em todo e qualquer objeto Java.
Vários tipos de objeto são ordenáveis. Ou seja, é possivel dizer que um objeto é maior, ou menor que outro. Alguns objetos têm uma única, e intrinseca, ordem possivel – a ordem natural ( ex.: Integer, String , Date , Money). Alguns objetos têm várias ordens possiveis ( ex.: Cliente, Produto )
Classes de objetos com ordem intrinseca devem implementar Comparable. Classes de objetos com várias ordens não devem implementar Comparable. Uma, ou mais, implementações de Comparator devem ser disponibilizadas à parte.
Comparator pode ser implementado para comparar qualquer tipo de objeto e vários podem ser implementados para comparar o mesmo tipo de objeto de formas diferentes. O método compare() substitui, para qualquer tipo de objeto, o uso dos operadores de comparação de tipos primitivos.
Comparable deve ser implementado apenas por objetos que têm uma ordem natural. Uma ordem instriseca.
Licença
Sérgio Taborda
Este trabalho é licenciado sob a Licença Creative Commons Atribuição-Uso Não-Comercial-Não a obras derivadas 3.0 Genérica . |
Era só para agradecer e felicitá-lo, porque a sua explicação sobre a Interface Comparable e Comparator foi bastante clara, objectiva e ajudou-me bastante a clarificar conceitos.
Obrigado
Bom artigo. Sobre igualdade, e as dificuldades de implementá-la corretamente, a Artima tem um artigo excelente: http://www.artima.com/lejava/articles/equality.html
Na verdade, a dificuldade de implementar equals e hashcode corretamente não se aplica apenas à java: se aplica à todas linguagens orientdas a objetos, ou à linguagens funcionais onde se deseje implementar funções equivalente em para alguma estrutura de dados.