왜 equals(), toString(), hashCode()를 오버라이딩 하는 걸까??
내가 생성한 클래스들은 Object를 최상위 클래스로 상속받고 있다.
그래서 내 클래스에서도 Object 클래스의 메서드를 사용할 수 있다.
Object 클래스 중 equals() toString() hashCode()을 왜 오버라이딩 해야하는지 궁금증이 생겨 정리해보았다.
equals() 메서드는 '객체간의 동등성 비교를 boolean 타입으로 반환' 하는 기능을 한다.
즉, 객체간의 '주소 값'을 비교한다.
public class Food {
public String food;
public int spicy;
public Food() {}
public Food(String food, int spicy) {
this.food = food;
this.spicy = spicy;
}
}
public class Eat {
public static void main(String[] arg) {
Food food = new Food("떡볶이", 3);
Food food2 = new Food("떡볶이", 5);
Food food3 = new Food("떡볶이", 3);
System.out.println(food == food2);
System.out.println(food == food3);
System.out.println(food2 == food3);
System.out.println(food.equals(food2));
System.out.println(food.equals(food3));
System.out.println(food2.equals(food3));
// 전부 false
}
}
위의 예제에 food 와 food3처럼 사람이 보기엔 같은 데 주소 값이 다르기에 false가 출력된다.
😵💫 ==와 .equals()가 헷갈려...
😃 일단 .equals()가 두 종류 있다는 걸 알아야한다.
String타입.equals()Object타입.equals()먼저 == 연산자는 두 객체의 주소 값을 비교한다.
String타입.equals()는 실제 값을 비교한다.
Object타입.equals()는 두 객체의 주소 값을 비교한다.
String타입.equals()는 equals()가 오버라이딩 되어있기 때문이다.
위의 예제처럼 .equals()를 통해 내용을 비교하고 싶다면 오버라이딩을 사용하면 해결된다. (음식 이름과 매운 정도가 같으면 같다고 정의한다.)
public class Food {
public String food;
public int spicy;
public Food() {}
public Food(String food, int spicy) {
this.food = food;
this.spicy = spicy;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Food) {
Food fd = (Food) obj;
if(this.food == fd.food && this.spicy == fd.spicy) {
return true;
} else {
return false;
}
}
return false;
}
}
public class Eat {
public static void main(String[] arg) {
Food food = new Food("떡볶이", 3);
Food food2 = new Food("떡볶이", 5);
Food food3 = new Food("떡볶이", 3);
System.out.println(food.equals(food2)); // false
System.out.println(food.equals(food3)); // true
System.out.println(food2.equals(food3)); // false
}
}
toString() 메서드는 '객체가 가지고 있는 값을 문자열로 반환' 하는 기능을 한다.
즉, 참조형 변수를 toString 하면 참조형 변수의 주소 값을 반환한다.
(정확히는 클래스명@주소 값 처럼 반환한다.)
public class Food {
public String food;
public Food() {}
public Food(String food) {
this.food = food;
}
}
public class Eat {
public static void main(String[] arg) {
Food favorite = new Food("떡볶이");
System.out.println(favorite); // Food@24d46ca6
System.out.println(favorite.toString()); // Food@24d46ca6
}
}
😕 왜 favorite은 favorite.toString()과 같은 값을 출력한걸까??
😀 System.out.print()을 사용할 때 참조형 변수를 넣으면
자동으로 toString()메서드가 작동한다.
✌️ 참고로 String과 File 클래스는 이미 재정의 되어있다.
public class Test {
public static void main(String[] arg) {
String food = "매운 떡볶이";
File foodFile = new File("C\\떡볶이 레시피.txt");
System.out.println(food); // 떡볶이
System.out.println(foodFile); // C\떡볶이레시피.txt
}
}
내가 만든 Food 클래스 또한 Object 클래스를 상속받고 있기에
오버라이딩을 사용해 Food만의 toString을 재정의 할 수 있다.
public class Food {
public String food;
public Food() {}
public Food(String food) {
this.food = food;
}
@Override
public String toString() {
return "나는 " + this.food + "를(을) 정말 좋아한다.";
}
}
public class Eat {
public static void main(String[] arg) {
Food favorite = new Food("떡볶이");
System.out.println(favorite); // 나는 떡볶이를(을) 정말 좋아한다.
System.out.println(favorite.toString()); // 나는 떡볶이를(을) 정말 좋아한다.
}
}
hashCode()가 제일 이해가 안갔다...
hashCode() 메서드는 '객체를 지칭하는 고유한 값을 정수 값으로 반환' 하는 기능을 한다.
즉, 객체의 주소 값이다.
hashCode() 메서드를 이해하기 힘들었던 부분은 equals()를 오버라이딩하면 hashCode() 또한 재정의를 꼭 해야한다는 부분이 어려웠다.
예시를 봐보자
public class Food {
public String food;
public int spicy;
public Food() {}
public Food(String food, int spicy) {
this.food = food;
this.spicy = spicy;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Food) {
Food fd = (Food) obj;
if(this.food == fd.food && this.spicy == fd.spicy) {
return true;
} else {
return false;
}
}
return false;
}
}
public class Eat {
public static void main(String[] arg) {
Food food = new Food("떡볶이", 3);
Food food2 = new Food("떡볶이", 5);
Food food3 = new Food("떡볶이", 3);
System.out.println(food.equals(food2)); // false
System.out.println(food.equals(food3)); // true
System.out.println(food2.equals(food3)); // false
System.out.println(food.hashCode()); // 6179012
System.out.println(food2.hashCode()); // 1159190
System.out.println(food3.hashCode()); // 9258584
}
}
food와 food3를 비교하면 true가 나온다.
하지만 .hashCode()를 사용하면 서로 다른 정수 값을 가진다.
🙄 결국 true를 출력했으니 된게 아닐까?
🤐 아니었다.
equals()와 hashCode()를 같이 재정의 해야되는 이유는
자바의 라이브러리, 컬렉션 프레임워크가 equals()와 hashCode() 둘 다 사용하기 때문이다.
위와 같은 문제는 .equals()만 재정의할 경우다.
아래와 같이 hashCode() 또한 재정의해주면 해결된다.
public class Food {
public String food;
public int spicy;
public Food() {}
public Food(String food, int spicy, int num) {
this.food = food;
this.spicy = spicy;
}
@Override
public int hashCode() {
return Objects.hash(food, spicy);
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Food) {
Food fd = (Food) obj;
if(this.num == fd.num) {
return true;
} else {
return false;
}
}
return false;
}
}
public class Eat {
public static void main(String[] arg) {
Food food = new Food("떡볶이", 3, 1);
Food food2 = new Food("떡볶이", 5, 2);
Food food3 = new Food("떡볶이", 3, 1);
System.out.println(food.equals(food2)); // false
System.out.println(food.equals(food3)); // true
System.out.println(food2.equals(food3)); // false
System.out.println(food.hashCode()); // 1433263365
System.out.println(food2.hashCode()); // 1433263367
System.out.println(food3.hashCode()); // 1433263365
}
}