equals() e hashCode()
Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy — lição 42 de 87.
Ao comparar objetos em Java, o operador == verifica se duas referências apontam para o mesmo local de memória. Para comparar o conteúdo real dos objetos, você precisa sobrescrever o método equals().
public class Person {
private String name;
private int age;
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && name.equals(person.name);
}
}O método equals() segue um padrão comum: primeiro verifique se é o mesmo objeto, depois verifique o tipo e, finalmente, compare os campos relevantes.
Sempre que você sobrescrever equals(), você também deve sobrescrever hashCode(). Este é um contrato no Java: objetos que são iguais devem ter o mesmo hash code. Coleções baseadas em hash como HashMap e HashSet dependem desta regra para funcionar corretamente.
@Override
public int hashCode() {
return Objects.hash(name, age);
}O método utilitário Objects.hash() gera um código hash a partir dos mesmos campos usados em equals(). Se dois objetos Person tiverem o mesmo nome e idade, eles serão considerados iguais e produzirão códigos hash idênticos, garantindo um comportamento consistente em coleções.
Desafio
FácilVamos construir um sistema de registro de estudantes que implementa adequadamente a igualdade de objetos. Você criará uma classe Student onde dois estudantes são considerados iguais se tiverem o mesmo ID de estudante e nome—independentemente de serem o mesmo objeto na memória.
Você organizará seu código em dois arquivos:
Student.java: Crie uma classe representando um estudante com três campos privados:studentId(String),name(String) egpa(double). Inclua um construtor para inicializar todos os campos e métodos getter para cada campo.Sobrescreva o método
equals()para que dois objetos Student sejam considerados iguais quando tiverem o mesmostudentIdE o mesmoname. Siga o padrão padrão: verifique se é a mesma referência, verifique se é nulo e o tipo, e então compare os campos relevantes.Sobrescreva o método
hashCode()usandoObjects.hash()com os mesmos campos usados emequals(). Lembre-se de importarjava.util.Objectsno topo do seu arquivo.Main.java: Dê vida à sua classe Student comparando objetos de estudantes. Você receberá seis entradas representando dois estudantes: o ID, nome e GPA do primeiro estudante, seguidos pelo ID, nome e GPA do segundo estudante.Crie ambos os objetos Student, realize três comparações e imprima os resultados:
- Imprima
Same reference:seguido por se o primeiro estudante é igual a si mesmo (deve ser sempre true) - Imprima
Content equality:seguido por se o primeiro estudante é igual ao segundo estudante (baseado na sua implementação de equals) - Imprima
Hash codes match:seguido por se ambos os estudantes possuem o mesmo hash code
- Imprima
Você receberá seis entradas: ID do primeiro estudante (String), nome do primeiro estudante (String), GPA do primeiro estudante (double), ID do segundo estudante (String), nome do segundo estudante (String) e GPA do segundo estudante (double).
Observe como, quando dois estudantes têm IDs e nomes correspondentes, tanto equals() retorna true QUANTO seus hash codes coincidem—este é o contrato que você deve manter!
Folha de consulta
O operador == verifica se duas referências apontam para o mesmo local na memória, não se os objetos possuem o mesmo conteúdo.
Para comparar o conteúdo de objetos, sobrescreva o método equals() seguindo este padrão:
@Override
public boolean equals(Object obj) {
if (this == obj) return true; // Mesma referência
if (obj == null || getClass() != obj.getClass()) return false; // Nulo ou tipo diferente
Person person = (Person) obj; // Cast para o tipo correto
return age == person.age && name.equals(person.name); // Compara os campos
}Sempre que você sobrescrever o equals(), você também deve sobrescrever o hashCode(). Este é um contrato do Java: objetos iguais devem ter o mesmo hash code.
@Override
public int hashCode() {
return Objects.hash(name, age);
}Use Objects.hash() com os mesmos campos usados em equals(). Isso garante que coleções baseadas em hash, como HashMap e HashSet, funcionem corretamente.
Experimente você mesmo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Ler os dados do primeiro aluno
String id1 = scanner.nextLine();
String name1 = scanner.nextLine();
double gpa1 = scanner.nextDouble();
scanner.nextLine(); // consumir a quebra de linha
// Ler os dados do segundo aluno
String id2 = scanner.nextLine();
String name2 = scanner.nextLine();
double gpa2 = scanner.nextDouble();
// TODO: Criar dois objetos Student usando os dados de entrada
// TODO: Imprimir "Same reference: " seguido de se student1 é igual a si mesmo
// TODO: Imprimir "Content equality: " seguido de se student1 é igual a student2
// TODO: Imprimir "Hash codes match: " seguido de se ambos os alunos possuem o mesmo hash code
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosA Palavra-chave thisMétodosCampos (Atributos)Método ConstrutorSobrecarga de ConstrutorRecapitulação - Calculadora Simples4Herança
Herança Básica (extends)A Palavra-chave superSobrescrita de Método (@Override)Encadeamento de ConstrutoresA Classe ObjectHerança Simples e MultinívelPor que não há Herança Múltipla de ClassesRecapitulação - Hierarquia de Funcionários7Métodos Especiais e a Classe Object
Método toString()equals() e hashCode()Método clone()compareTo() e ComparableInterface ComparatorRecapitulação - Ordenação Personalizada2Modificadores de Acesso e Encapsulamento
Visão Geral dos Níveis de AcessoMétodos Getter e SetterOcultação de InformaçõesA Palavra-chave finalRecapitulação - Gerenciador de Conta Bancária5Polimorfismo
Fundamentos de Sobrecarga de MétodosSobrescrita de Métodos (Tempo de Execução)Upcasting e DowncastingO Operador instanceofClasses e Métodos AbstratosRecapitulação - Calculadora de Formas8Conceitos Avançados de OOP
Composição vs HerançaAgregação vs ComposiçãoClasses Internas, Aninhadas e AnônimasEnums e Métodos de EnumRecords (Java 16+)Classes Seladas (Java 17+)11Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão BuilderPadrão ObserverPadrão Strategy3Propriedades de Classe e Membros Estáticos
Variáveis de Instância vs EstáticasMétodos EstáticosBlocos EstáticosConstantes (static final)Recapitulação - Contador e Utilitário6Interfaces e Classes Abstratas
Introdução às InterfacesImplementando InterfacesImplementação de Múltiplas InterfacesDefault e Static em InterfacesClasses Abstratas vs InterfacesInterfaces FuncionaisRecapitulação - Sistema de Pagamento9Generics
Introdução a GenericsClasses GenéricasMétodos GenéricosParâmetros de Tipos DelimitadosWildcards (?, extends, super)Recapitulação - Container Genérico12Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Iterator