Java: equals( ) & hashCode( ) 오버라이드

0
post-thumbnail

Java: equals() & hashcode() 오버라이드

객체들이 ‘어떤 기준’으로 동일한지 아닌지를 판단해야 할 필요가 있을 때 사용한다.

생각해보자.
어떤 객체들이 동일한 클래스에서 나왔다고 할 때, 같은 클래스에서 나온 것만으로도 같은 객체?
동일한 클래스를 통해 생성되었지만, 그 값이 다르다면 다른 객체?
그렇다면 그 값이란 어떤 값?
그러니까, 특정 상황에서 ‘객체가 같다는 기준’을 정의할 필요가 생길 것이다.

그럴 때 equals()hashcode() 를 오버라이드 해준다.

/* 데이터 클래스 */
public class Toy {
	private String name;
	private int price;
	private String manufacturing;
	
	public Toy(String name, int price, String manufacturing) {
		this.name = name;
		this.price = price;
		this.manufacturing = manufacturing;
	}
}
/* 실행 클래스 */
public class ToyEqualTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Toy gun = new Toy("gun", 15000, "Lockheed Martin");
		Toy waterGun = new Toy("gun", 15000, "Lockheed Martin");
		
		System.out.println(gun.equals(waterGun));
	}
}
>> 실행결과
false

내부값은 동일하지만, 다른 객체로 판정한다.

당연한 것이다. 참조 자료이며 주소로 판정하는 것이니까.

하지만 가진 값이 동일할 때, 같은 객체로 판단하도록 만들고 싶다.
즉, 같은 값을 가지고 있는지 알고 싶을 때와 같은 상황에서 equals()를 재정의 할 필요가 생긴다.

public class Toy {
	private String name;
	private int price;
	private String manufacturing;
	
	public Toy(String name, int price, String manufacturing) {
		this.name = name;
		this.price = price;
		this.manufacturing = manufacturing;
	}
	
	@Override
	public boolean equals(Object obj) {
		boolean flag = false;
		if(obj instanceof Toy) {
			if(		((Toy) obj).name != null && ((Toy) obj).name == this.name &&
					((Toy) obj).price == this.price &&
					((Toy) obj).manufacturing != null && ((Toy) obj).manufacturing == this.manufacturing){
				return true;
			}
		}
		return false;
	}/* end equals */
}

instanceof 메서드를 사용하여 객체 타입을 확인한다.

Object instanceof Class

Object 객체가 Class에 일부인지, 속하는지를 boolean타입으로 반환한다.

  • 여기서는 매개 변수로 받는 객체가 Toy와 같은 타입일 때, 해당 객체를 Toy로 캐스팅한다
  • 그 후 해당 객체의 필드값들을 하나씩 받아와서 비교한다
  • 모든 필드 값이 같다면 true를 반환한다

실행 클래스에서 확인해본다.

/* 실행 클래스 */
public class ToyEqualTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Toy gun = new Toy("gun", 15000, "Lockheed Martin");
		Toy waterGun = new Toy("gun", 15000, "Lockheed Martin");
		Toy fireGun = new Toy("Fire Gun", 15000, "Ragnaros");
		
		System.out.println(gun.equals(waterGun));
		System.out.println(gun.equals(fireGun));
	
	}
}
>> 실행결과
true
false

equals()를 재정의 할 때는 반드시 hashCode()를 반드시 같이 재정의 한다.

해시코드란?
객체를 식별하는 정수값을 말한다.

  • Object 클래스의 hasCode()는 객체의 메모리 번지를 이용해서 해시코드를 만들어 리턴한다

  • 따라서 객체마다 다른 값을 지니고 있다

  • 논리적 동등 비교 시 hashCode()를 오버라이딩 할 필요가 생긴다

  • HashSet, HashMap, Hashtable은 다음과 같은 방법으로 객체가 동일한지를 비교한다
    1. hashCode() 리턴값이 동일하다
    1-O. equals() 리턴값이 동일하다
    1-O-O. 동등 객체

논리적으로 두 인스턴스가 같다면, 같은 해시코드를 반환해야할 것이다.

import java.util.Objects;
	@Override    
	public int hashCode() {
		return Objects.hash(name, price, manufacturing);
	}
}

위와 같이 간단하게 재정의 할 수 있다.
주어지는 매개값으로 배열을 생성하고, 이 배열의 해시코드를 얻고 리턴한다.
hash() 메서드는 특정 클래스가 hashCode()를 재정의할 때 리턴값의 생성에 사용하면 유용하다.

public class ToyEqualTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Toy gun = new Toy("gun", 15000, "Lockheed Martin");
		Toy waterGun = new Toy("gun", 15000, "Lockheed Martin");
		Toy fireGun = new Toy("Fire Gun", 15000, "Ragnaros");
		
		System.out.println(gun.equals(waterGun));
		System.out.println(gun.equals(fireGun));
		System.out.println(waterGun.equals(fireGun));
	
	}
}
>> 실행 결과
true
false
false

hashCode()를 오버라이딩 했을 때, 실제 해당 인스턴스의 주소값을 알고 싶다면?
System.identity.HashCode() 메서드를 사용한다.

음! Collection은 좀 더 자세히 볼 필요가 있겠다!


참고자료
모든 분들께 감사드립니다
Objects 클래스(3) : 해시코드 생성 hash(), hashCode() / 알통몬의 인생
[Java] equals() & hashcode() 메서드는 언제 재정의해야 할까? / sonypark
[JAVA] hashCode()메서드 오버라이딩 / kekim20

0개의 댓글