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 |
|---|---|---|
|
Comparação segura de dois objetos considerando null | |
|
Cálculo conciso do hash code a partir de vários campos | |
|
Comparação via Comparator, segura para null | |
|
Verificação de null; lança NullPointerException | |
|
Verificação de null/não null (útil no Stream API) | |
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.
GO TO FULL VERSION