Fala, pessoal. Hoje vamos falar sobre hashcode e equals em Java, que gera muita confusão para entender.

Antes de mais nada, tudo bem que a sua IDE implemente pra você. No entanto, acho que muitos tem curiosidade de saber como funciona, porquê e quando usar.

Equals()

Primeiramente, vamos começar pelo equals(). Equals é um método da classe lang.Object usado para comparar dois objetos. Portanto, esse trecho de código retornará true, certo?

public class App {
    public static void main(String[] args) throws Exception {
        Produto celular1 = new Produto("Samsung note 10", 1000.0);
        Produto celular2 = new Produto("Samsung note 10", 1000.0);

        System.out.println(celular1.equals(celular2));
    }
}

Errado. Por padrão, o método equals só retornará true se os dois objetos apontarem pro mesmo endereço de memória. Por isso, mesmo que os valores sejam iguais, o retorno será false. Portanto, para funcionar como esperamos, necessitamos sobrescrever o método equals na classe Produto.

Existem alguns princípios definidos pelo próprio Java EE sobre a implementação do equals:

  • Consistente: para quaisquer objetos x e y, o valor de x.equals(y) só poderá ser diferente se as propriedade em equals() mudem.
  • Transitivo: para quaisquer objetos x, y e , se x.equals(y) retorna true e y.equals(z) retorna true, então x.equals(z) deve retornar true.
  • Simétrico: dados dois objetos x e y, x.equals(y) deve retornar true se e somente se y.equals(x) retorna true.
  • Reflexivo: um objeto x deve ser igual a ele mesmo, ou seja, retornar true no x.equals(x).

HashCode()

Já um hashcode(código hash) é um valor inteiro associado com todos os objetos em Java. Então, para obter esse hashcode precisamos utilizar o método hashCode(), esse método retornará um inteiro para o objeto passado. Sendo assim, se dois objetos possuem o mesmo código hash, provavelmente são iguais ou no mínimo parecidos(a depender da implementação).

public class App {
    public static void main(String[] args) throws Exception {
        Produto celular1 = new Produto("Samsung note 10", 1000.0);
        Produto celular2 = new Produto("Samsung note 10", 1000.0);

        System.out.println(celular1.hashCode() + " | " + celular2.hashCode());
    }
}

// output: 1562557367 | 1101288798

Como vimos no equals, essa comparação deu false, portanto os códigos hash devem ser diferentes e são.

Implementando nosso equals() e hashcode()

Portanto, agora que vimos o comportamento padrão do equals e vimos o que é o hashcode, vamos implementá-los da nossa forma. E nós faremos isso na classe Produto:

public class Produto {
    
    private String nome;
    private Double preco;
    
    // Construtor
    public Produto(String nome, Double preco) {
        this.nome = nome;
        this.preco = preco;
    }
 
    public Produto() {
    }

    // Getters and setters
    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public Double getPreco() {
        return preco;
    }

    public void setPreco(Double preco) {
        this.preco = preco;
    }

    // Equals e hashcode()
       
}

Mas por que sobrescrever os dois métodos e não só o equals? Porque com os dois implementados, o equals só será executado nos objetos que tiverem o mesmo código hash, o que reforça o desempenho.

Primeiro, vou sobrescrever o hashCode() . Eu o implementei de forma a retornar o comprimento do nome do Produto, logo, o código hash será esse comprimento:

    @Override
    public int hashCode() {
        return this.nome.length();
    }

// Executando a classe App dnv
Produto celular1 = new Produto("Samsung note 10", 1000.0);
Produto celular2 = new Produto("Samsung note 10", 1000.0);
System.out.println(celular1.hashCode() + " | " + celular2.hashCode());
// output: 15 | 15

Códigos hash iguais, mas o equals ainda vai retornar false, vamos mudar isso:

    @Override
    public boolean equals(Object obj) {
        Produto objeto = (Produto) obj;
        return this.nome.equals(objeto.getNome()) &&   this.preco.equals(objeto.getPreco());
    }

Agora, se o nome e o preço forem iguais, equals retornará true; independente de apontarem para o mesmo lugar na memória.

É importante lembrar que o método equals só será executado nos objetos que tiverem o mesmo código hash, que definimos para ser o comprimento do nome do produto.

Implementação da IDE

Apesar de podermos sobrescrever os métodos nós mesmos, não é uma prática recomendada. As IDEs como Eclipse, IntelliJ, VSCode e etc; implementam pra gente, fazendo checagens importantes pra que fique tudo de acordo com os princípios do Java.

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((nome == null) ? 0 : nome.hashCode());
        result = prime * result + ((preco == null) ? 0 : preco.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Produto other = (Produto) obj;
        if (nome == null) {
            if (other.nome != null)
                return false;
        } else if (!nome.equals(other.nome))
            return false;
        if (preco == null) {
            if (other.preco != null)
                return false;
        } else if (!preco.equals(other.preco))
            return false;
        return true;
    }

Viram como fica mais rebuscado? Também fica mais seguro quando trabalhamos com aplicações grandes, já que essa implementação foi amplamente testada. Ao gerar na sua IDE, você escolhe que atributos serão usados para definir o código hash. Nesse caso escolhi todos os atributos(nome e preco).

Conclusão

Assim, quando quiser fazer comparações de objeto por valor, não deixe de implementar o hashCode() e o equals() para evitar surpresas no retorno do seu equals.

No nosso curso de Java o professor trabalha em cima desses conceitos com exemplos. Não deixe de conferir se deseja aprender a linguagem: https://www.cod3r.com.br/courses/java-2020-completo. Até mais!