Strona główna Java C++ Hardware HTML i javascript Java Software SQL flag_united_kingdom flag_poland

Java – nadpisanie metod equals() i hashCode() w stworzonych klasach.

Dodano: 2011-07-22

Klasy które posiadają pola nie statyczne i będą użyte w kolekcjach, warto wyposażyć (na drogę w świat) w nadpisane metody equals() oraz hashCode() („modyfikacja” jednej wymusza modyfikacje drugiej). Zapewnia to zwracanie prawidłowych wyników w przypadku porównywania obiektów za pomocą metody equals().

Nie należy także ulegać przeświadczeniu, że nie użycie klasy w kolekcjach sprawia, że metoda equals() dziedziczona z klasy Object działa w nich prawidłowo ;) Dlatego warto ją przetestować, a w przypadku problemów nadpisać metody equals() oraz hashCode() .

Niektóre środowiska programistyczne umożliwiają automatyczne wygenerowanie nadpisanych metod dla stworzonej klasy. W przypadku Eclipse w ciele klasy wciśnij Shift+Alt+S i wybierz z meni kontekstowego Generate hashCode() and equals()....

Najlepiej zobaczyć to na przykładzie, metody wygenerowane przez Eclipse, zwróć uwagę na klase CDog:

Drukuj Zaznacz kod
CEquals_hashCode_override.java
import java.util.HashSet;
import java.util.Set;


public class CEquals_hashCode_override  {
	public static void main(String args[]){
		System.out.println("Przeciążanie metody equals() i hashCode()");
		
		Set<CDog> dog = new HashSet<CDog>();
		dog.add(new CDog("Alaskan Malamute", "Funny"));
		dog.add(new CDog("St. Bernard", "Bethoveen"));
		
		System.out.println(dog.contains(new CDog("Alaskan Malamute", "Funny")) 
				+ " - new CDog(\"Alaskan Malamute\", \"Funny\")");
		System.out.println(dog.contains(new CDog("Alaskan Malamute", "Fun"))
				+ " - new CDog(\"Alaskan Malamute\", \"Fun\")");
		System.out.println(dog.contains(new CDog(null, null)) 
				+ " - new CDog(\"null\", \"null\")");
		
		String t0 = new String("tekst");
		String t1 = new String("tekst");
		System.out.println("t0: " + t0.hashCode());
		System.out.println("t1: " + t1.hashCode());
		
		
		CDog dog0 = new CDog("Alaskan Malamute", "Funny");
		CDog dog1 = new CDog("Alaskan Malamute", "Funny");
		System.out.println("dog0: " + dog0.hashCode());
		System.out.println("dog1: " + dog1.hashCode());
		
		System.out.println(dog0.equals(dog1));
	}
	
}

class CDog {
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((nameDog == null) ? 0 : nameDog.hashCode());
		result = prime * result + ((breed == null) ? 0 : breed.hashCode());
		return result;
	}



	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		CDog other = (CDog) obj;
		if (nameDog == null) {
			if (other.nameDog != null)
				return false;
		} else if (!nameDog.equals(other.nameDog))
			return false;
		if (breed == null) {
			if (other.breed != null)
				return false;
		} else if (!breed.equals(other.breed))
			return false;
		return true;
	}



	public CDog(String breed, String nameDog){
		this.setNameDog(nameDog);
		this.setBreed(breed);
	}
	
	
	
	public String getBreed() {
		return breed;
	}
	
	public void setBreed(String breed) {
		this.breed = breed;
	}
	
	public String getNameDog() {
		return nameDog;
	}
	
	public void setNameDog(String nameDog) {
		this.nameDog = nameDog;
	}
	
	private String breed;
	private String nameDog;
}

Zakomentuj metody wszystkie i niektóre nadpisane i zobacz co się stanie.

Wygenerowana metoda equals() przez Eclipse działa prawidłowo, warto ją oprogramować po swojemu po to by lepiej ją zrozumieć.

Użycie if (getClass() != obj.getClass()) zamiast if (!(obj instanceof CDog)) ma duże znaczenie. W pierwszym przypadku jeżeli stworzylibyśmy klasę CSmallDog, która by dziedziczyła z klasy CDog i stworzyli dwa obiekty CSmallDog sdog oraz CDog dog i wykonali dog.equals(sdog); to otrzymamy wynik false. W przypadku użycia instanceof otrzymalibyśmy wynik true, co może oszukać programistę ;)

Stosując słowo kluczowe instanceof należy usunąć sprawdzanie czy obiekt jest nullem, ponieważ w przypadku zgodności zostanie zwrócona wartość false bez rzucenia wyjątku.

Przykładowa metoda z zastosowaniem instanceof może wyglądać tak:

@Override
	public boolean equals(Object obj){
		if (obj == this)
			return true;

		if (!(obj instanceof CDog))
		return false;
		
		CDog other = (CDog) obj;
		return ((nameDog == null && other.nameDog == null) || nameDog.equals(other.nameDog)) 
				&& ((breed == null && other.breed == null) || breed.equals(other.breed));
	}