Szkolenia programowania we Wrocławiu
Kurs Java Podstawy - rozszerzony

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.

  1. Metoda equals() a klasa Object
  2. Przykład wykorzystania
  3. equals() i klasa String
  4. Generowanie metody equals

    1. Eclipse
    2. Intellij IDEA

  5. Kontrakt equals() i hashCode()

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

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() 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.

Komentarze

Komentarze zamknięte. Zapraszamy do grupy na Facebooku