CodeGym /Cursos /JAVA 25 SELF /Classe Objects: métodos equals, hashCode, hash

Classe Objects: métodos equals, hashCode, hash

JAVA 25 SELF
Nível 29 , Lição 1
Disponível

1. Introdução à classe Objects

Vamos ao assunto! Se você está cansado de verificar manualmente valores contra null e escrever o cálculo do hash para cada campo, a classe utilitária java.util.Objects vai ajudar. Sua missão — tornar o trabalho com objetos mais simples, conciso e seguro.

É realmente um “canivete suíço”: a classe consegue comparar objetos por igualdade com segurança (sem risco de receber NullPointerException), calcular hash codes de forma conveniente, comparar via um Comparator e verificar argumentos contra null.

Objects.equals: comparação segura considerando null

Se você escrever apenas a.equals(b) e a for null, virá um NullPointerException. As verificações manuais são verbosas. Objects.equals(a, b) faz isso por você:

  • Se ambos forem null — retorna true.
  • Se exatamente um for null — retorna false.
  • Se ambos não forem null — chama o equals normal.
import java.util.Objects;

String a = null;
String b = "Java";
System.out.println(Objects.equals(a, b)); // false

String c = null;
System.out.println(Objects.equals(a, c)); // true

String d = "Java";
String e = "Java";
System.out.println(Objects.equals(d, e)); // true

Por que isso é útil? O código fica mais curto, mais limpo e protegido de NPEs acidentais.

2. Objects.hash e hashCode: cálculo de hash conciso

Ao sobrescrever hashCode junto com equals, é fácil cometer erros, especialmente quando há muitos campos. O código manual costuma ficar verboso e frágil:

@Override
public int hashCode() {
    int result = 17;
    result = 31 * result + (name != null ? name.hashCode() : 0);
    result = 31 * result + age;
    return result;
}

O método Objects.hash resolve isso — curto, seguro e com suporte a null:

import java.util.Objects;

public class Person {
    private String name;
    private int age;

    // ... construtor, getters etc.

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

Ponto importante: Objects.hash usa varargs e cria um array — em raros trechos de alto desempenho um hashCode manual pode ser mais rápido. Para a maioria dos aplicativos, a diferença é irrelevante.

3. Objects.compare: delegando a comparação para o Comparator

Às vezes é preciso comparar dois objetos usando um Comparator já preparado. Em vez de chamar diretamente comparator.compare(a, b), você pode usar:

int result = Objects.compare(a, b, comparator);

Este método:

  • Retorna 0 se os objetos forem iguais.
  • Considera null “menor” do que qualquer objeto não-null.
  • Nos demais casos, delega a lógica ao Comparator fornecido.
import java.util.Comparator;
import java.util.Objects;

class Person {
    private String name;
    Person(String name) { 
        this.name = name; 
    }
    public String getName() { 
        return name; 
    }
}

public class Main {
    public static void main(String[] args) {
        Person a = new Person("Anna");
        Person b = new Person("Boris");
        Comparator<Person> byName = Comparator.comparing(Person::getName);

        System.out.println(Objects.compare(a, b, byName)); // <0, pois "Anna" < "Boris"
        System.out.println(Objects.compare(a, null, byName)); // >0, pois a != null
        System.out.println(Objects.compare(null, b, byName)); // <0, pois null < b
        System.out.println(Objects.compare(null, null, byName)); // 0
    }
}

4. Objects.requireNonNull: proteção contra erros “invisíveis”

Se um método deve aceitar apenas valores não-null, verifique isso imediatamente. Objects.requireNonNull lançará NullPointerException com a sua mensagem:

public void setName(String name) {
    this.name = Objects.requireNonNull(name, "O nome não pode ser null");
}

5. Exemplo: implementação correta de equals, hashCode e compareTo com Objects

import java.util.Objects;

public class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = Objects.requireNonNull(name, "O nome não pode ser null");
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true; // Comparação por referência
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        // Comparação segura com null
        return age == person.age && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age); // Conciso e seguro
    }

    @Override
    public int compareTo(Person other) {
        // Primeiro comparamos pelo nome, depois pela idade
        int cmp = name.compareTo(other.name);
        if (cmp != 0) return cmp;
        return Integer.compare(age, other.age);
    }
}

Agora é possível armazenar objetos em HashSet, usá-los como chaves em HashMap, comparar por igualdade e ordenar listas (por exemplo, com Collections.sort).

6. Aplicação em cenários reais: menos código e menos erros

Exemplo: lista de usuários no aplicativo

Graças ao par correto equals/hashCode, a busca em coleções funciona de maneira previsível:

import java.util.ArrayList;
import java.util.List;

List<Person> users = new ArrayList<>();
users.add(new Person("Anna", 25));
users.add(new Person("Boris", 30));

Person search = new Person("Anna", 25);
System.out.println(users.contains(search)); // true

Exemplo: trabalhando com campos que podem ser null

Se uma classe tem campos que podem ser null (por exemplo, o nome do meio), use Objects.equals e Objects.hash:

import java.util.Objects;

public class User {
    private String firstName;
    private String middleName; // Pode ser null
    private String lastName;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(firstName, user.firstName)
            && Objects.equals(middleName, user.middleName)
            && Objects.equals(lastName, user.lastName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(firstName, middleName, lastName);
    }
}

7. Tabela: principais métodos da classe Objects

Método Finalidade Exemplo de uso
Objects.equals(a, b)
Comparação segura de dois objetos considerando null
Objects.equals(a, b)
Objects.hash(a, b, ...)
Cálculo conciso do hash code a partir de vários campos
Objects.hash(name, age)
Objects.compare(a, b, comparator)
Comparação via Comparator, segura para null
Objects.compare(p1, p2, byNameComparator)
Objects.requireNonNull(obj[, msg])
Verificação de null; lança NullPointerException
Objects.requireNonNull(name, "O nome não pode ser null")
Objects.isNull(obj) / Objects.nonNull(obj)
Verificação de null/não null (útil no Stream API)
list.stream().filter(Objects::nonNull)

8. Erros típicos ao usar os métodos da classe Objects

Erro nº 1: esqueceu de usar Objects.equals para campos que podem ser null. Se você comparar campos diretamente via equals, pode acabar com um NullPointerException. Use Objects.equals(middleName, other.middleName).

Erro nº 2: nem todos os campos foram considerados em hashCode. Os campos que participam de equals devem participar também de hashCode; caso contrário, o comportamento de HashSet/HashMap se tornará imprevisível.

Erro nº 3: hashCode manual com erro. Respeitar fatores e verificações de null não é trivial. Objects.hash faz isso por você; use-o se não houver requisitos rigorosos de desempenho.

Erro nº 4: não usar Objects.requireNonNull onde isso é parte do contrato da classe. Se um campo não admite null, verifique-o no construtor/setter — o erro surgirá imediatamente, e não “nas profundezas” da pilha de chamadas.

Erro nº 5: usar Objects.hash para arrays. Para arrays, use Arrays.hashCode, e para arrays aninhados — Arrays.deepHashCode; de forma análoga, para comparar conteúdo existem Arrays.equals/Arrays.deepEquals.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION