Metoda equals()
Spis treści
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
}
}
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.
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.
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.