Article image

HS

Hiago Soares09/02/2024 00:05
Compartilhe

Funções em Java: compreendendo a função equals

    Introdução

    Em muitas situações durante o desenvolvimento de software em Java, surge a necessidade de comparar objetos para determinar igualdade. Para isso, a função equals desempenha um papel crucial. Dessa forma, abordarei nesse artigo o funcionamento desse método, além de esclarecer uma dúvida bastante comum sobre seu uso.

    Sobre a função

    O método equals pertence à classe Object e é utilizado para determinar se dois objetos são idênticos ou não. Como, com exceção dos tipos primitivos, tudo em Java é um objeto (herda da classe Object), todas as classes possuem esse método.

    Além disso, a função equals possui uma implementação padrão definida pelo Java e que geralmente é sobrescrita (override) para ser utilizada nas classes criadas pelo usuário seguindo algum critério de comparação.

    Funcionamento

    Implementação padrão

    De acordo com a documentação Java, a implementação padrão do método equals é:

    public boolean equals(Object obj) {
      return (this == obj);
    }
    

    Isso significa que, se não for sobrescrito, a comparação será feita considerando a igualdade por endereço de memória dos objetos. É possível conferir isso com o exemplo a seguir.

    Considere a classe Person (pessoa):

    public class Person {
      private String name;
      private int age;
    
      public Person(String name, int age) {
          this.name = name;
          this.age = age;
      }
    }
    

    Observe o que ocorre quando criamos dois objetos dessa classe e tentamos compará-los sem ter sobrescrito equals:

    Person person1 = new Person('Alice', 25);
    Person person2 = new Person('Alice', 25);
    System.out.println(person1.equals(person2));
    
    # Output:
    # false
    

    A implementação padrão é utilizada e o resultado é falso porque new cria um novo objeto com endereço de memória diferente dos demais, isto é, o endereço de memória de person1 é diferente do endereço de memória de person2.

    Implementação personalizada

    Obviamente não faz sentido algum comparar uma pessoa com outra considerando o endereço de memória. Digamos que o critério de comparação seja o nome e a idade. Deve-se, então, sobrescrever o método equals especificando esses critérios.

    public class Person {
      private String name;
      private int age;
    
    
      public Person(String name, int age) {
          this.name = name;
          this.age = age;
      }
    
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }
    

    Com isso, a comparação feita anteriormente será verdadeira.

    System.out.println(person1.equals(person2));
    
    # Output:
    # true
    

    Explicando o código

    if (this == o) return true; verifica se o objeto atual (this) é o mesmo objeto que o objeto passado como argumento (o). Se forem a mesma instância na memória, não é necessário fazer mais comparações, então retorna true.

    if (o == null || getClass() != o.getClass()) return false; verifica se o objeto passado como argumento (o) é nulo ou se ele não é uma instância da mesma classe que this. Se qualquer uma dessas condições for verdadeira, os objetos não podem ser iguais, então retorna false.

    Person person = (Person) o; faz um downcast do objeto o para a classe Person, pois o é do tipo Object. Isso é seguro porque verificamos anteriormente se o é uma instância de Person.

    return age == person.age && Objects.equals(name, person.name); compara os campos age dos objetos this e o, verificando se são iguais. Além disso, compara os campos name usando Objects.equals() para verificar se eles são iguais, levando em consideração a possibilidade de name ser nulo em um dos objetos.

    Você deve ter notado duas questões importantes nesse trecho de código: a comparação com == e o uso de Objects.equals. Não se preocupe, tudo será explicado:

    Objects.equals() é um método da classe utilitária Objects que geralmente é utilizado para comparar atributos de classes, pois considera a igualdade de null como verdadeira. Sua implementação, de acordo com a documentação Java é:

    public static boolean equals(Object a, Object b) {
      return (a == b) || (a != null && a.equals(b));
    }
    

    E quanto a age == person.age ? Bom, para isso, é preciso lembrar do que foi dito no início do artigo. O método equals está disponível apenas em objetos, o que não é o caso dos tipos primitivos, que não são objetos nem possuem métodos. Dessa forma, para comparar tipos primitivos, o == é utilizado.

    O uso do == é gerador de uma dúvida bastante comum que em breve esclarecerei.

    Características

    De acordo com a documentação Java, a implementação de equals deve satisfazer as seguintes diretrizes para objetos não nulos:

    image

    1. É reflexivo: para qualquer valor de referência x, x.equals() deve retornar true;
    2. É simétrico: para qualquer valor de referência x e y, x.equals(y) deve retornar true se, e somente se, y.equals(x) retornar true.
    3. É transitivo: para qualquer valor de referência de x, y e z, se x.equals(y) retornar true e y.equals(z) também retornar true, então, x.equals(z) deve retornar true.
    4. É consistente: para qualquer valor de referência de x e y, múltiplas chamadas de x.equals(y) retornarão consistentemente true ou consistentemente false, contanto que nenhuma informação usada nas comparações dos objetos de equals tenha sido alterada.
    5. Para qualquer valor de referência x, x.equals(null) deve retornar false.

    Uma dúvida comum

    Uma dúvida bastante comum entre desenvolvedores Java é a diferença entre a função equals e o uso do == .

    Em se tratando de objetos, a diferença é simples. Com uma implementação personalizada de equals, equals realiza a comparação de acordo com o critério definido enquanto == realiza a comparação considerando o endereço de memória dos objetos.

    Com a implementação padrão, o uso de equals é equivalente ao uso de ==, isto é, compara considerando o endereço de memória dos objetos.

    Em se tratando de tipos primitivos, equals não é disponível, e a comparação é feita considerando o valor e utilizando == .

    Considerações

    Para facilitar o uso da linguagem, algumas classes bastante utilizadas já possuem uma implementação personalizada de equals, como String, Integer, Double, Long, entre outras.

    A título de curiosidade, essa é a utilizada pela classe String no Java 17:

    public boolean equals(Object anObject) {
      if (this == anObject) {
          return true;
      }
      return (anObject instanceof String aString)
              && (!COMPACT_STRINGS || this.coder == aString.coder)
              && StringLatin1.equals(value, aString.value);
    }
    

    Além disso, cabe ressaltar que uma prática quase obrigatória é a implementação personalizada da função equals junto com a da função hashCode, mas que não abordarei neste artigo pois foge do escopo.

    Conclusão

    Quando se pretende comparar objetos, entender o funcionamento da função equals, suas diferenças e particularidades é fundamental para se tornar um desenvolvedor Java que domina a linguagem.

    Referências

    https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals-java.lang.Object-

    https://docs.oracle.com/javase/8/docs/api/java/util/Objects.html#equals-java.lang.Object-java.lang.Object-

    https://ioflood.com/blog/dot-equals-method-java/#:~:text=equals()%20method%20is%20primarily,any%20two%20objects%20in%20Java.

    https://www.devmedia.com.br/sobrescrevendo-o-metodo-equals-em-java/26484

    https://medium.com/@erayaraz10/when-to-use-equals-and-when-to-use-in-java-best-practices-6915b3fc9983

    Compartilhe
    Comentários (0)