1. Comparando objetos em Java
Em Java, os objetos podem ser comparados tanto por referência quanto por valor.
Comparando referências
Se duas variáveis apontam para o mesmo objeto na memória, então as referências armazenadas nessas variáveis são iguais. Se você comparar essas variáveis usando o operador de igualdade ( ==
), obterá true e esse resultado fará sentido. Tudo é simples aqui.
Código | Saída do console |
---|---|
|
|
Comparando por valor
Mas muitas vezes você pode encontrar situações em que duas variáveis se referem a dois objetos distintos que são idênticos. Por exemplo, dois objetos strings diferentes que contêm o mesmo texto.
Para determinar se objetos diferentes são idênticos, use o equals()
método. Por exemplo:
Código | Saída do console |
---|---|
|
|
O equals
método não está limitado à String
classe. Toda aula tem.
Mesmo aulas que você escreve por conta própria, e aqui está o porquê.
2. Object
classe
Todas as classes em Java herdam a Object
classe. Os criadores do Java criaram essa abordagem.
E se uma classe herda a Object
classe, ela ganha todos os métodos da Object
classe. E esta é uma das principais consequências da herança.
Em outras palavras, toda classe tem os métodos da Object
classe, mesmo que seu código não os mencione.
Esses métodos herdados incluem métodos relacionados à comparação de objetos. Estes são os métodos equals()
e hashCode()
.
Código | Na realidade, eis o que teremos: |
---|---|
|
|
No exemplo acima, criamos uma Person
classe simples com os parâmetros name e age, mas não um único método. Mas como todas as classes herdam a Object
classe, a Person
classe possui automaticamente dois métodos:
Método | Descrição |
---|---|
|
Compara o objeto atual e o objeto passado |
|
Retorna o hashcode do objeto atual |
Acontece que absolutamente todo objeto tem o equals
método e objetos de diferentes tipos podem ser comparados entre si. Esse código irá compilar e funcionar perfeitamente.
Código | Saída do console |
---|---|
|
|
|
|
3. equals()
método
O equals()
método, herdado da Object
classe, implementa o algoritmo mais simples para comparar o objeto atual com os objetos passados: ele apenas compara as referências aos objetos.
Você obtém o mesmo resultado se apenas comparar Person
variáveis em vez de chamar o equals()
método. Exemplo:
Código | Saída do console |
---|---|
|
|
Quando o equals
método é chamado a
, ele simplesmente compara a referência armazenada na a
variável com a referência armazenada na b
variável.
No entanto, a comparação funciona de maneira diferente para a String
classe. Por que?
Porque o pessoal que criou a String
classe escreveu sua própria implementação do equals()
método.
Implementação do equals()
método
Agora vamos escrever nossa própria implementação do método equals na Person
classe. Vamos considerar 4 casos principais.
equals
método, sempre leva um Object
objeto como argumento
Cenário 1 : o mesmo objeto no qual o equals
método é chamado também é passado para o equals
método. Se as referências do objeto atual e do objeto passado forem iguais, o método deve retornar true
. Um objeto é igual a si mesmo.
No código ficará assim:
Código | Descrição |
---|---|
|
Comparar referências |
Cenário 2 : null
é passado para o equals
método — não temos nada para comparar. O objeto no qual o equals
método é chamado definitivamente não é nulo, então precisamos retornar false
neste caso.
No código ficará assim:
Código | Descrição |
---|---|
|
Compare referências É o objeto passado null ? |
Cenário 3 : uma referência a um objeto que não é a Person
é passada para o equals
método. O Person
objeto é igual ao não- Person
objeto? Essa é uma questão para o desenvolvedor da Person
classe decidir como quiser.
Mas geralmente os objetos devem ser da mesma classe para serem considerados iguais. Portanto, se algo diferente de um objeto da Person
classe for passado para o nosso método equals, sempre retornaremos false
. Como você pode verificar o tipo de um objeto? Isso mesmo — usando o instanceof
operador.
Aqui está a aparência do nosso novo código:
Código | Descrição |
---|---|
|
Compare referências É o objeto passado null ? Se o objeto passado não for um Person |
4. Comparando dois Person
objetos
Com o que terminamos? Se chegamos ao fim do método, então temos uma Person
referência de objeto que não é null
. Portanto, convertemos em a Person
e comparamos os dados internos relevantes de ambos os objetos. E esse é o nosso quarto cenário .
Código | Descrição |
---|---|
|
Compare referências É o objeto passado null ? Se o objeto passado não for um Person Typecasting |
E como você compara dois Person
objetos? São iguais se tiverem o mesmo nome ( name
) e idade ( age
). O código final ficará assim:
Código | Descrição |
---|---|
|
Compare referências É o objeto passado null ? Se o objeto passado não for um Person Typecasting |
Mas isso não é tudo.
Primeiro, o campo de nome é um String
, então você precisa comparar o campo de nome chamando o equals
método.
this.name.equals(person.name)
Em segundo lugar, o name
campo pode ser null
: nesse caso, você não pode invocá equals
-lo. Você precisa de uma verificação adicional para null
:
this.name != null && this.name.equals(person.name)
Dito isso, se o campo de nome estiver null
em ambos Person
os objetos, os nomes ainda serão iguais.
O código para o quarto cenário pode ser assim:
|
Se as idades não forem iguais, imediatamente return false Se this.name for igual a null , não adianta comparar pelo equals método. Aqui, o segundo name campo é igual a null , ou não é. Compare os dois campos de nome usando o equals método. |
5. hashCode()
método
Além do equals
método, que se destina a realizar uma comparação detalhada de todos os campos de ambos os objetos, existe outro método que pode ser utilizado para uma comparação imprecisa, mas muito rápida: hashCode()
.
Imagine que você está classificando alfabeticamente uma lista de milhares de palavras e precisa comparar pares de palavras repetidamente. E as palavras são longas, compostas por muitas letras. De um modo geral, essa comparação levaria muito tempo.
Mas pode ser acelerado. Suponha que temos palavras que começam com letras diferentes - fica imediatamente claro que são diferentes. Mas se eles começam com as mesmas letras, ainda não podemos dizer qual será o resultado: as palavras podem ser iguais ou diferentes.
O hashCode()
método funciona usando um princípio semelhante. Se você chamá-lo em um objeto, ele retornará algum número — análogo à primeira letra de uma palavra. Este número tem as seguintes propriedades:
- Objetos idênticos sempre têm o mesmo hashcode
- Diferentes objetos podem ter o mesmo hashcode ou seus hashcodes podem ser diferentes
- Se os objetos tiverem hashcodes diferentes, então os objetos são definitivamente diferentes
Para tornar isso ainda mais claro, vamos reformular essas propriedades em termos de palavras:
- Palavras idênticas sempre têm as mesmas primeiras letras.
- Palavras diferentes podem ter as mesmas primeiras letras ou suas primeiras letras podem ser diferentes
- Se as palavras tiverem primeiras letras diferentes, então as palavras são definitivamente diferentes
A última propriedade é usada para acelerar a comparação de objetos:
Primeiro, os hashcodes dos dois objetos são calculados. Se esses hashcodes forem diferentes, os objetos são definitivamente diferentes e não há necessidade de compará-los mais.
Mas se os hashcodes forem os mesmos, ainda teremos que comparar os objetos usando o método equals.
6. Contratos em código
O comportamento descrito acima deve ser implementado por todas as classes em Java. Durante a compilação, não há como verificar se os objetos são comparados corretamente.
Os programadores Java têm um acordo universal de que, se escreverem sua própria implementação do método equals() e, assim, substituir a implementação padrão (na Object
classe), eles também devem escrever sua própria implementação do hashCode()
método de forma que as regras mencionadas sejam satisfeito.
Esse arranjo é chamado de contrato .
Se você implementar apenas o equals()
ou apenas o hashCode()
método em sua classe, estará violando grosseiramente o contrato (você quebrou o acordo). Não faça isso.
Se outros programadores usarem seu código, ele pode não funcionar corretamente. Além do mais, você usará um código que depende da adesão aos contratos acima.
Ao pesquisar um elemento, todas as coleções Java primeiro comparam os hashcodes dos objetos e só então realizam uma comparação usando o equals
método.
Isso significa que, se você fornecer um equals
método para sua própria classe, mas não escrever seu próprio hashCode()
método ou implementá-lo incorretamente, as coleções podem não funcionar corretamente com seus objetos.
Por exemplo, você pode adicionar um objeto a uma lista e procurá-lo usando o contains()
método, mas a coleção pode não localizar seu objeto.
GO TO FULL VERSION