우리가 클래스를 선언하고 구현할 때 따로 상속을 하지 않는다면 자동적으로
java.lang.Object
클래스가 상속되게 됩니다. 또한 Object의java.lang
패키지는 import 를 하지 않더라도 컴파일러에 의해 자동으로 import가 되어집니다. 그래서 모든 클래스는 Object 클래스의 자식이거나 자손인 클래스로 정의할 수 있는데요. 그렇기 때문에 최상위 클래스인 Object 클래스에 있는 대표적인 메서드를 아는 것이 중요합니다. 이번 포스팅에선 Object의 대표적인 메소드인 toString(), equals(), hashCode() 메서드에 대해서 공부해봅시다 ☺️
Object 클래스의 toString() 메소드는 객체의 문자 정보를 리턴합니다. 여기서 객체의 문자 정보란 객체를 문자열로 표현한 값을 말합니다.
@return: a string representation of the object.
실제로 Object 클래스의 toString() 메소드의 코드를 보면 다음과 같습니다.
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
보시는 것 처럼 class 의 이름과, hashCode 값을 반환해주는 것을 확인할 수 있습니다.
예제 코드를 통해서 실제로 어떻게 출력되는지 확인해보겠습니다.
static class MyObject {
public MyObject() {}
}
public static void main(String[] args) {
MyObject obj = new MyObject();
System.out.println(obj.toString());
}
출력 결과
package명.StringStudy$MyObject@3f0ee7cb
다음과 같이 toString()이 정상적으로 동작하는 것을 확인할 수 있습니다.
우리는 Object 의 toString() 메서드를 overriding 하여 활용할 수 있는데요.
주로 toString() 메소드의 오버라이딩 목적은 "객체의 정보를 문자열 형태로 표현" 하는 것에 있습니다.
이미 java 패키지의 다양한 클래스에서 toString() 메소드를 오버라이딩하여 표현하고 있습니다. String 클래스는 toString() 메소드를 오버라이딩하여 자기 자신의 값을 return 하는 코드로 구현되어 있습니다.
String Class - toString()
public String toString() {
return this;
}
예시 코드
String str= "Hi, I'm String";
System.out.println(str.toString());
// 결과 :
// Hi, I'm String
이처럼 우리는 클래스를 선언하고 구현할 때 toString()을 오버라이딩함으로써 객체의 정보를 문자열로 표현하는데 효과적으로 사용할 수 있습니다.
Object 클래스의 equals() 메소드는 두 객체의 값이 같은지 다른지에 대한 boolean 값은 반환해주는 메소드입니다.
String strA = new String("abc");
String strB = new String("abc");
System.out.println(strA == strB);
System.out.println(strA.equals(strB));
// 결과 :
// false
// true
다음과 같이 물리적으로 메모리 주소가 같지 않더라도, 값이 동일하면 true를 반환해주는 것을 확인할 수 있습니다.
Object 클래스의 equals() 내부 코드는 다음과 같습니다.
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String aString = (String)anObject;
if (coder() == aString.coder()) {
return isLatin1() ? StringLatin1.equals(value, aString.value)
: StringUTF16.equals(value, aString.value);
}
}
return false;
}
Object 클래스의 equals() 메소드를 오버라이딩하는 주목적은
"물리적으로 다른 메모리 주소를 갖는 객체이더라도, 논리적으로 동일한지 여부를 반환" 하기 위해서라고 할 수 있습니다.
위 목적에 맞게 작성한 예시코드를 통해 어떻게 equals() 메소드가 오버라이딩 되는지 확인해 보겠습니다.
public class Drink{
private String name;
private String kind;
public Drink(String name, String kind) {
this.name = name;
this.kind = kind;
}
public String getKind() {
return kind;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Drink) {
return this.getKind() == ((Drink)obj).getKind();
} else {
return false;
}
}
}
"마실 것" 을 저장하는 클래스인 Drink를 다음과 같이 정의하였습니다.
"이름(name)이 달라도 종류(kind) 같으면 같다" 라는 논리적 동일성을 재정의하기 위해 equals() 메소드를 다음과 같이 오버라이딩 하였습니다.
Drink drink1 = new Drink("삼다수", "물");
Drink drink2 = new Drink("오아시스", "물");
System.out.println(drink1.equals(drink2));
// 결과 :
// true
다음과 같이 정상적으로 우리가 오버라이딩한 결과대로 작동하는 것을 확인할 수 있습니다.
Object 클래스의 hashCode() 메소드는 객체 해시코드를 반환해주는 메서드 입니다. 여기서 객체 해시코드란 객체를 식별할 하나의 정수값을 의미합니다. Object 클래스의 hashCode() 는 객체의 메모리 주소를 이용해서 해시코드를 만들어 리턴하기 때문에 객체마다 다른 값을 가지고 있습니다.
내부적으로는 JVM이 인스턴스를 생성할 때 메모리 주소를 10진수로 변환하여 해시코드를 부여합니다. 따라서 실제 메모리 주소와는 별개의 값이므로 실제 메모리 주소를 알고 싶을 때는 System 클래스의 identityHashCode()로 확인할 수 있습니다.
Object 클래스의 hashCode() 메소드를 사용한 예시 코드는 다음과 같습니다.
Drink drink1 = new Drink("삼다수", "물");
Drink drink2 = new Drink("오아시스", "물");
System.out.println(drink1.hashCode());
System.out.println(drink2.hashCode());
// 결과 :
// 1057941451
// 1975358023
Object 클래스의 hashCode() 메소드는 "물리적으로 다른 메모리 주소를 갖는 두 객체에 동일성을 부여한다" 라는 목적으로 오버라이딩 할 수 있습니다.
위 목적에 맞게 작성한 예시코드를 통해 어떻게 hashCode() 메소드가 오버라이딩 되는지 확인해 보겠습니다.
public static class Drink{
private String name;
private String kind;
public Drink(String name, String kind) {
this.name = name;
this.kind = kind;
}
public String getKind() {
return kind;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Drink) {
return this.getKind() == ((Drink)obj).getKind();
} else {
return false;
}
}
@Override
public int hashCode() {
return getKind().hashCode();
}
}
equals()와 마찬가지로 "이름(name)이 달라도 종류(kind) 같으면 같다" 라는 논리적 동일성을 재정의하기 위해 hashCode() 메소드를 다음과 같이 오버라이딩 하였습니다.
System.out.println(drink1.hashCode());
System.out.println(drink2.hashCode());
// 결과 :
47932
47932
다음과 같이 동일한 해시코드 값이 출력되어지는 것을 확인할 수 있습니다.
Java에서 객체의 동등성 검사는 hashCode()와 equals() 메소드를 통하여 수행합니다. 로직은 다음과 같습니다.
hashCode()
값 비교equals()
리턴 값객체의 동등성 검사 순서도
동등성 검사 시, equals() 메소드는 해시 값을 사용하여 객체를 비교하는 hashCode()에 비해 느리므로, 좀 더 빠른 성능을 위해선 hashCode()를 사용해야 합니다.
우리가 클래스를 선언하고 구현할때 객체의 동등성 검사는 중요합니다. 그러므로 위와 같은 동등성 검사에 로직을 이해하고, 클래스를 구현할 때 hashCode() 와 equals() 메소드를 적절히 오버라이딩할 필요가 있습니다.
자바 Object 클래스 정리 - toString(), equals(), hashCode(), clone()
안녕하세요 잘 읽다갑니다 ㅎㅎ
혹시 글 내용 중 equals 메소드 설명 부분에서
Object 클래스의 equals 메소드의 내부 코드가 적혀있는 게 아니라
String 클래스의 equals 메소드의 내부 코드가 적혀있는 거 같은데 아닌가요 ??