Metoda equals()


Metoda equals() służy w Javie do porównywania typów obiektowych. W odróżnieniu od typów prostych operator == w przypadku typów obiektowych porównuje referencje, a nie równość strukturalną obiektów, z tego powodu porównanie dwóch obiektów poprzez:

obiekt1 == obiekt2

w większości przypadków jest nieskuteczne i otrzymujemy wynik, którego nie oczekiwaliśmy.

Metoda equals() a klasa Object

Metoda equals() zdefiniowana jest w klasie Object, a ponieważ jest to klasa, po której dziedziczą wszystkie klasy Javy, to możemy ją wywołać na dowolnym obiekcie. Domyślna implementacja metody equals() sprowadza się jednak jedynie do porównania referencji ==, czyli w większości przypadków nie jest zbyt użyteczna. W klasach, których obiekty będziemy ze sobą porównywali powinniśmy ją nadpisać.

Przykład wykorzystania

Załóżmy, że w naszym projekcie istnieje klasa Product przechowująca informacje o nazwie i cenie produktu.

public class Product {
     private String name;
     private double price;
     public Product(String name, double price) {
         this.name = name;
         this.price = price;
     }
     public String getName() {
         return name;
     }
     public void setName(String name) {
         this.name = name;
     }
     public double getPrice() {
         return price;
     }
     public void setPrice(double price) {
         this.price = price;
     }
 }

Jeżeli utworzymy dwa obiekty przechowujące identyczną nazwę oraz cenę i spróbujemy je porównać za pomocą operatora == lub metody equals() odziedziczonej z klasy Object, to w wyniku dwa razy otrzymamy false. Dzieje się tak dlatego, że dwie referencje wskazujące na dwa różne obiekty nie są sobie równe.

public class Shop {
     public static void main(String[] args) {
         Product product1 = new Product("Milk", 2.5);
         Product product2 = new Product("Milk", 2.5);
         System.out.println(product1 == product2); //false
         System.out.println(product1.equals(product2)); //false
     }
 }
compare

Kurs Java

Zazwyczaj przy porównywaniu obiektów będziemy chcieli jednak sprawdzić ich równość strukturalną (czy przechowują te same informacje) i w tym celu należy nadpisać metodę equals.

public class Product {
     private String name;
     private double price;
 //gettery settery, konstruktor
 
     @Override
     public boolean equals(Object obj) {
         if (this == obj)
             return true;
         if (obj == null)
             return false;
         if (getClass() != obj.getClass())
             return false;
         Product other = (Product) obj;
         if (name == null) {
             if (other.name != null)
                 return false;
         } else if (!name.equals(other.name))
             return false;
         if (Double.doubleToLongBits(price) != Double.doubleToLongBits(other.price))
             return false;
         return true;
     }
 }

W metodzie equals powinniśmy:

  • porównać wartość referencji
  • sprawdzić, czy przekazany jako argument obiekt nie jest wartością null
  • sprawdzić, czy przekazany obiekt jest tego samego typu co obiekt, w na którym operujemy, a jeśli tak to dokonać rzutowania
  • porównać poszczególne pola obiektu zwracając uwagę na to, czy nie są one wartościami null i a jeśli są typami obiektowymi, to porównywać je także poprzez metode equals, a nie ==

Taki schemat postępowania sprawi, że metoda equals będzie wydajna, np. porównanie product1.equals(product1) zwróci true już na pierwszym warunku więc reszta nie będzie sprawdzana.

public class Shop {
     public static void main(String[] args) {
         Product product1 = new Product("Milk", 2.5);
         Product product2 = new Product("Milk", 2.5);
         System.out.println(product1 == product2); //false
         System.out.println(product1.equals(product2)); //true
     }
 }

equals() i klasa String

Klasa String jest specyficznym typem obiektowym, w którym w niektórych sytuacjach porównanie przy pomocy operatora == zwróci true. Stanie się tak zawsze wtedy, kiedy obiekty tworzymy poprzez literały lub użyjemy metody intern().

String first = "Napis";
 String second = "Napis";
 System.out.println(first == second); //true
 System.out.println(first.equals(second)); //true

Jeśli jednak napisy wczytywane są do naszej aplikacji np. z pliku, konsoli, czy innego źródła, to porównanie przy pomocy == będzie już niewystarczające.

Scanner scan = new Scanner(System.in);
 String first = scan.nextLine(); //"abc"
 String second = scan.nextLine(); //"abc"
 System.out.println(first == second); //false
 System.out.println(first.equals(second)); //true

Generowanie metody equals()

Generowanie metody equals() w Eclipse

Metoda equals() jest tam schematyczna i często wykorzystywana, że na szczęście da się ją wygenerować w każdym popularnym środowisku. Zamiast ją generować można także skorzystać z biblioteki Lombok.

W eclipse należy skorzystać z zakładki Source > Generate hashCode() and equals() lub użyć skrótu klawiaturowego Alt + Shift + S.

equals eclipse

Generowanie metody equals() w IntelliJ IDEA

Podobna opcja dostępna jest w Intellij IDEA, jednak tym razem wybieramy opcję Code > Generate > equals() and hashCode() lub można użyć skrótu Alt + Insert.

equals intellij

Kontrakt equals() i hashCode()

Zauważ, że środowiska zawsze generują dwie metody i oprócz equals tworzona jest dodatkowo hashCode. Pomiędzy tymi zachodzi kontrakt mówiący o tym, że jeżeli dwa obiekty porównywane przy pomocy equals zwracają true, to ich metody hashCode powinny zwrócić równe sobie wartości.


Dyskusja i komentarze

Masz pytania do tego wpisu? Może chcesz się podzielić spostrzeżeniami? Zapraszamy dyskusji na naszej grupie na Facebooku.