본문 바로가기
언어/자바의 정석

[09-1] equals( ), hashCode( ), toString( ) - Object클래스

by chan10 2021. 2. 25.

Object클래스

Object클래스는 모든 클래스의 조상이기에 Object클래스의 멤버들은 모든 클래스에서 바로 사용이 가능하다.

o  Object클래스는 멤버 변수 없이 오직 11개의 메서드만 가지고 있다. 이 메서드들은 모든 인스턴스가 가져야 할 기본적인 것들이다.

Object클래스의 메서드 설명
protected Object clone( ) 객체 자신의 복사본을 반환한다.
public boolean equals(Object obj) 객체 자신과 객체 obj가 같은 객체인지 알려준다.(같으면 true)
protected void finalize( ) 객체가 소멸될 때 가비지 컬렉터에 의해 자동적으로 호출된다.
이 때 수행 되어야하는 코드가 있을 때 오버라이딩한다. (거의 사용안함)
public Class getClass( ) 객체 자신의 클래스 정보를 담고 있는 Class인스턴스를 반환한다.
public int hashCode( ) 객체 자신의 해시코드를 반환한다.
public String toString( ) 객체 자신의 정보를 문자열로 반환한다.
public void notify( ) 객체 자신을 사용하려고 기다리는 쓰레드를 하나만 깨운다.
public void notifyAll( ) 객체 자신을 사용하려고 기다리는 모든 쓰레드를 깨운다.
public void wait( )
public void wait(long timeout)
public void wait(long timeout, int nanos)
다른 쓰레드가 notify( )나 notifyAll( )을 호출할 때까지 현재 쓰레드를 무한히 또는 지정된 시간(timeout, nanos)동안 기다리게한다.
(timeout은 천분의 1초, nanos는 109 분의 1초)

  

equals(Object obj)

o  매개변수로 객체의 참조변수를 받아서 주소 값으로 비교하여 결과를 boolean으로 알려준다.

o  두 객체의 같고 다름을 참조변수의 값으로 판단하기에 두 객체를 equals메서드로 비교하면 항상 false의 결과를 얻는다.

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
}

 

public class Test {    
    public static void main(String[] args) {    
        Value v1 = new Value(10);
        Value v2 = new Value(10);
        
        System.out.println(v1.equals(v2)?"v1과 v2는 같다.":"v1과 v2는 다르다.");
        v1=v2;
        System.out.println(v1.equals(v2)?"v1과 v2는 같다.":"v1과 v2는 다르다.");        
    }
}
 
class Value {
    int value;
    
    Value(int value) {
        this.value = value;
    }
}

- 두 Value의 인스턴스의 멤버 변수 value가 같더라도 equals는 주소 값으로 비교하기에 false의 결과가 나온다.

- 그러나 v1=v2;을 수행 후 v2의 주소 값이 v1이 참조하고 있는 인스턴스의 주소 값으로 변경되기에 equals의 결과가 true로 나온다.

 

public class Test {   
    public static void main(String[] args) {    
        Person p1 = new Person(8011081111222L);
        Person p2 = new Person(8011081111222L);
        
        System.out.println((p1==p2)?"p1과 p2는 같은 사람입니다.":"p1과 p2는 다른 사람입니다.");
        System.out.println((p1.equals(p2))?"p1과 p2는 같은 사람입니다.":"p1과 p2는 다른 사람입니다.");
    }
}
 
class Person {
    long id;
    
    Person(long id) {this.id=id;}
    
    @Override
    public boolean equals(Object obj) {
        if(obj instanceof Person) {
        //obj가 Object타입이기에 id값을 참조하기 위해서 Person타입으로 형변환이 필요하다.
            return id==((Person)obj).id;    
        } else {
            return false;
        }
    }
}

- equals메서드를 오버라이딩해서 주소가 아닌 객체에 저장된 내용을 비교하도록 변경하였다.

- 이렇게 함으로써 서로 다른 인스턴스 일지라도 같은 id를 갖고 있다면 true결과로 얻게 할 수 있다.

 

String클래스 역시 Object의 equals메서드를 오버라이딩 하여 String인스턴스가 갖는 문자열 값을 비교하도록 되어있다.

o  String클래스 뿐만 아니라 Date, File, wrapper클래스 (Integer, Double 등)의 equals메서드도 주소 값이 아닌 내용을 비교하도록 되어있다. 그러나 StringBuffer클래스는 오버라이딩 되어 있지 않다.

 

 

 hashCode( )

o  이 메서드는 해싱(hashing)기법에 사용되는 ‘해시 함수(hash function)’를 구현한 것이다. 해싱은 데이터 관리 기법의 하나로 대량의 데이터를 저장하고 검색하는데 유용하다.

o  해시함수는 찾고자 하는 값을 입력하면, 그 값이 저장된 위치를 알려주는 해시코드를 반환한다.

o  Object클래스에 정의된 hashCode메서드는 객체의 주소 값으로 해시 코드를 만들어 반환한다.

o  따라서 같은 객체라면 hashCode메서드를 호출했을 때 결과값의 해시 코드가 같아야 하기 때문에 클래스의 인스턴스 변수 값으로 객체의 같고 다름을 판단해야 한다면 equals메서드 뿐만 아니라 hashCode메서드도 오버라이딩을 해줘야 한다.

 

public class Test {    
    public static void main(String[] args) {    
        String str1 = new String("abc");
        String str2 = new String("abc");
        
        System.out.println(str1.equals(str2));
        System.out.println(str1.hashCode());
        System.out.println(str2.hashCode());
        System.out.println(System.identityHashCode(str1));
        System.out.println(System.identityHashCode(str2));
    }
}

- String클래스는 문자열이 같으면 동일한 해시 코드를 반환하도록 hashCode메서드가 오버라이딩 되어 있다.

- System.identityHashCode은 객체의 주소 값을 해시 코드로 반환하기에 객체 마다 다른 해시 코드가 반환된다.

 

 

toString( )

o  toString( )메서드는 인스턴스에 대한 정보를 문자열(String)로 제공할 목적으로 정의한 것이다.

o  클래스 작성 시 오버라이딩을 하지 않는 다면 이래와 같이 클래스 이름에 16진수의 해시 코드가 반환된다.

public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
public class Test {    
    public static void main(String[] args) {    
        String str =  new String("KOREA");
        java.util.Date today = new java.util.Date();
        
        System.out.println(str);
        System.out.println(str.toString());
        System.out.println(today);
        System.out.println(today.toString());
    }
}

- toString메서드에 대해 String클래스는 인스턴스가 갖고 있는 문자열을 반환하도록, Date클래스는 인스턴스가 갖고 있는 날짜와 시간을 문자열로 변환하여 반환하도록 오버라이딩 되어 있다.

 

public class Test {    
    public static void main(String[] args) {    
        Card c1 = new Card();
        Card c2 = new Card();
        System.out.println(c1.toString());
        System.out.println(c2.toString());
    }
}
 
class Card {
    String kind;
    int number;
    
    Card(){this("SPACE",1);}
    Card(String kind, int number){
        this.kind = kind;
        this.number = number;
    }
    
    @Override
    // toString() 오버라이딩
    public String toString() {
        return "kind : "+kind+", number : "+number;
    }
}

- toString( )메서드를 오버라이딩 하여 인스턴스가 갖고 있는 변수 값을 문자열로 반환하도록 하였기에 변수의 값이 출력되었다.

- Object클래스에서 toString( )의 접근 제한 범위는 public이기에 오버라이딩 시에도 public으로 해야 한다. (조상의 접근 범위보다 같거나 넓어야 한다.)