왜 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
}
}