Object 클래스 속 toString()과 equals() 오버라이딩
Object의 내용을 많이 담고 있진 않으나 Object의 toString()과 equals()를 다루기에 내용이 지저분해질 것을 우려하여 게시글을 따로 작성하였다. 앞서 작성한 "상속 객체 형변환..." 게시글과 이어진다.
1. Object 클래스
Object 클래스는 자바 내부에 존재하는 클래스로 개발자가 만드는 모든 클래스의 부모 클래스이다. java.lang 패키지안에 존재하며, 개발자가 클래스 하나를 정의하면 그 클래스에 자동으로 상속된다.
class UserClass /* extends Object */ { }
따로 작성하지 않아도, 숨겨져 있을 뿐 상속관계인 것이다.
2. Object와 toString() 메소드
모든 사용자 클래스에서 상속되는 Object 클래스에는 toString() 메소드가 있다. toString()은 ' 패키지이름.클래스이름@해시코드 ' 를 String 형식으로 리턴한다. 메소드는 " 객체의 참조변수.toString() " 형태로 사용해야 하지만, toString()은 " .toString()" 을 생략하고 객체의 참조변수만으로 사용 가능하다.
class UserClass /* extends Object */ { }
UserClass obj = new UserClass();
System.out.println(obj.toString());
// 객체의 참조변수.toString() 으로 사용해야 맞지만, .toString()은 생략 가능함
System.out.println(obj);
toString()은 오버라이딩되어 각 클래스에 맞는 String을 리턴받는 형태로 사용된다. 특이하게도 String 클래스 안에서 toString()은 나름의 오버라이딩이 되어 있어 아래와 같이 String 작성하면 지정된 String을 출력 받을 수 있다.
String s1 = new String("가나다라마바사");
String s2 = "아자차카타파하";
System.out.println(s1);
System.out.println(s2);
우리가 계속 사용하던 String 변수의 값 지정 방식이 사실은 toString()의 오버라이딩된 메소드 사용이었던 것이다.
추가로 Object 클래스에는..
// getClass 메소드는 해당 객체의 클래스 이름을 리턴하는 메소드, Object 클래스에서 상속 받은 메소드
System.out.println(obj.getClass());
객체의 클래스 이름을 리턴하는 메소드 getClass() 라던지..
// hasCode 메소드는 해당 객체의 해시코드값(다른 객체와 구분하기 위한 고유값)을 리턴해주는 메소드(JVM에 의해서 관리되고 있는 번호)
System.out.println(obj); // 16진수
System.out.println(obj.hashCode()); // 10진수
객체의 10진수 형태의 해시코드값을 출력하는 메소드 hashCode() 도 있다.
3. toString() 오버라이딩 활용 예시
toString을 오버라이딩해 활용한 예시로
class Point{
private int x;
private int y;
Point(int x, int y){
this.x = x;
this.y = y;
}
public String toString() {
// this : 매개변수로 같은 이름이 들어올 것을 방지
return "x:" + this.x + ", y:" + this.y;
}
}
Point 클래스
public static void main(String[] args) {
Point p = new Point(30, 20);
System.out.println("Point p의 좌표값 -> " + p.toString()); // 화면 표시 : point p의 좌표값 -> x:30, y:20
String msg = "Point p의 좌표값 -> " + p;
System.out.println(msg);
}
main 메소드
입력받은 x, y 좌표값을 toString() 메소드를 오버라이딩하여 출력했다.
toString은 String msg = "Point p의 좌표값 -> " + p; 과 같이 객체의 이름을만으로도 사용할 수 있다.
4. equals 오버라이딩을 통한 객체 비교
equals 오버라이딩을 도대체 왜 해야하는지 이해하지 못했는데, 타입캐스팅을 공부하다보니 눈에보이더라..
Object 클래스 안에는 equals라는 객체 비교 메소드도 존재한다. String 객체를 비교할 때 다룬 메소드로, 오버라이딩 하기 전에는 참조값(주소값)끼리 비교하는 동작을 한다.
class Line{
private int x;
private int y;
Line(int x, int y){
this.x = x;
this.y = y;
}
public String toString() {
String result = "x:" + this.x + ", y:" + this.y;
return result;
}
}
Line 클래스
// new : 서로 다른 공간에 생성, 다른 주소
Line obj1 = new Line(20, 30);
Line obj2 = new Line(20, 30);
main 메소드
만약 main 메소드에 있는 obj1과 obj2를 equals로 비교하면 false가 나올 것이다. new를 통해 다른 주소에 객체가 생성되었기 때문이다. eqauls는 주소값을 비교하니까.
그런데 만약 위와 같은 클래스와 객체 선언을 하고나서, Line 클래스의 멤버변수 x와 y 값이 같은지를 통해 obj1과 obj2 가 같음을 비교하고 싶다면 어떻게 해야할까?
객체 내의 멤버변수 값들을 이용해서 비교를 하고자할 때는 equals 메소드를 오버라이딩 해야한다.
public boolean equals(Object obj) { // obj1 -> this obj2 -> 매개변수 obj
if(!(obj instanceof Line)) return false;
// 접근하려면 Line형태로 타입캐스팅 해야 함
Line a = (Line)obj;
// x와 y를 사용하려면 하위클래스인 Line형의 참조변수에 참조값을 옮겨줌
boolean result = (this.x == a.x) && (this.y == a.y);
return result;
}
우선 Line 클래스 속 멤버변수 x와 y에 접근하기 위해서는 Line 형태로 타입캐스팅을 해야한다. Object는 사용자가 클래스를 정의할 때 자동으로 '상위클래스로써' 상속된다. 그렇기에 상위클래스의 참조변수 Object obj로 전달되는 Line obj2의 참조값은 하위클래스의 인스턴스 주소가 된다. (이전 포스팅 참고) 상위클래스 참조변수에 하위클래스 인스턴스 주소를 대입하는 것은 가능하지만, 대입은 가능해도 상위클래스 참조변수인 obj는 하위클래스인 Line의 메소드에 접근하지 못한다.
한마디로 매개변수에 Object 참조변수가 있으면 해당 매개변수를 받는 메소드에는 어떤 클래스의 인스턴스 주소도 전달이 가능하다. 다만, 멤버변수나 메소드를 사용하기 위해서 타입캐스팅을 해야할 뿐.
if(obj1.equals(obj2)) System.out.println("obj1와 obj2는 같습니다.");
else System.out.println("obj1와 obj2는 다릅니다.");
헷갈리는 개념이니 다시 한 번 정리를 하자면,
Object 클래스에는 객체를 비교하는 메소드 equals가 존재한다. 오버라이딩 하기 전에는 참조값(주소값)끼리 비교하는 동작을 한다. 그러나 객체 내의 멤버변수 값을 참조하기 위해서는 자료형이 같아야하는데, 매개변수를 Object 형태로 받는 equals 메소드는 매개변수(Object)가 상위클래스일 경우 자료형이 맞지 않아 멤버변수를 참조하지 못한다. 따라서 객체의 매개변수를 참조하기 위해서는 equals를 오버라이딩하여 해당 클래스에 맞게 재정의 해야한다.
5. equals 오버라이딩 활용 예시
class Circle{
private int x;
private int y;
private int radius;
Circle(int x, int y, int r){
this.x = x;
this.y = y;
this.radius = r;
}
public String toString() {
return "x좌표: " + this.x + ", y좌표: " + this.y + ", 반지름: " + this.radius;
}
public boolean equals(Object obj) {
if(!(obj instanceof Circle)) return false;
Circle target = (Circle)obj;
boolean result1 = this.x == target.x;
boolean result2 = this.y == target.y;
boolean result3 = this.radius == target.radius;
boolean result = result1 && result2 && result3;
return result;
}
}
Circle 클래스
public static void main(String[] args) {
Circle c1 = new Circle(5, 7, 10);
Circle c2 = new Circle(5, 6, 10);
Circle c3 = new Circle(6, 9, 5);
System.out.println("원 c1의 정보 - " + c1);
System.out.println("원 c2의 정보 - " + c2);
System.out.println("원 c3의 정보 - " + c3);
if(c1.equals(c2))
System.out.println("c1 변수와 c2 변수는 같습니다.");
else
System.out.println("c1 변수와 c2 변수는 다릅니다.");
if(c1.equals(c3))
System.out.println("c1 변수와 c3 변수는 같습니다.");
else
System.out.println("c1 변수와 c3 변수는 다릅니다.");
}
main 메소드
6. Object와 상속
앞서 개발자가 클래스 하나를 정의하면 그 클래스에 Object 클래스가 자동으로 상속된다는 이야기를 했다.
그런데 자바의 클래스는 한 클래스 당 하나의 클래스만 상속(extends) 가능하다. 만약 다른 클래스를 extends 를 이용해 Object가 아닌 다른 클래스를 상속하면 그 클래스에서는 extends Object가 지워지게 된다. 그러면 Object 클래스 속 다양한 메소드들은 어떻게 사용하나?
... 싶겠지만 상속받은 상위클래스가 이미 Object를 상속하는 꼴이기 때문에 결국 또 다시 Object의 하위(자식이자 손자..)클래스가 된다.
'Backend > Java' 카테고리의 다른 글
[JAVA] 제네릭(Generic) (0) | 2024.10.17 |
---|---|
[JAVA] Collection 클래스와 Vector, ArrayList (0) | 2024.10.17 |
[JAVA] 상속 객체 형변환(TypeCasting)과 사용 (0) | 2024.10.16 |
[JAVA] 상속(extends)과 super, 오버라이딩(overriding) (0) | 2024.10.15 |
[JAVA] 싱글톤 패턴(Singleton Pattern)과 초기화 블록 (0) | 2024.10.14 |