[Java] Object(toString(), equals(), hashCode())
Java

[Java] Object(toString(), equals(), hashCode())

반응형

Object가 모든 클래스의 조상 클래스는 사실을 사람들이 잘 모를 수 있다.

모든 클래스가 Object를 상속받고 있지만, 생략되어 있기 때문에 관련 내용을 보지 않았다면 알 수가 없다.

나 또한 처음에는 알지 못했다.

그래서 모든 객체는 Object의 메소드모두 호출해 사용할 수 있다.

11개의 메서드로 구성되어 있는데, 이 중 몇 가지를 살펴보고자 한다.

 

 

toString()

- 기본 동작 : 객체의 해시코드 출력(클래스명@16진수해시코드)

 

equals() 메소드 다음으로 가장 많이 사용했던 메소드다.

나는 문자열이 아닌 변수를 문자열로 변환시킬 때 많이 사용했다.

그래서 당연히 문자열을 리턴하는 역할로 알고 있었지만 원형은 달랐다.

 

예시를 살펴보자.

아래와 같이 객체에 toString()을 붙여 출력하면 클래스명+@+해시코드 값이 나온다.

이것이 toString() 메소드의 원래 출력 값이다.

public class ObjectExample {
    public static void main(String[] args) {
        final ObjectExample s1 = new ObjectExample();
        System.out.println(s1.toString()); // ObjectExample@26f0a63f
    }
}

 

 

그렇다면 내가 자주 사용했던 String 클래스에서 사용해보자.

String 객체에 toString() 메소드를 붙여 출력했지만, 위와 다른 결과가 나왔다.

다른 형식으로 나오는 이유는 String 클래스가 toString() 메소드를 오버라이딩해 저장하고 있는 문자열을 반환하기 때문이다.

*오버라이딩 : 부모클래스의 메소드를 재정의해 사용하는 것.

public class ObjectExample {
    public static void main(String[] args) {
        String s2 = new String("aa");
        System.out.println(s2.toString()); //aa
        System.out.println(s2); //aa
    }
}

 

String을 주로 사용했던 나는 원래 기능을 오해할 수 있지만, '클래스명@16진수해시코드'을 반환하는 역할이라는 것을 잊지 말자!

 

 

 

equals()

- 기본 동작: '==' 연산 결과 반환

 

아무 생각 없이 제일 많이 사용했던 메소드..

String 객체를 서로 비교할 때 제일 많이 사용하는 메소드일 것이다.

 

예시를 먼저 보자.

동일한 내용을 넣은 객체인데 equals() 메소드를 사용하면 false가 나온다.

그 이유는 Object 클래스의 equals() 메소드를 사용하는데, '==' 연산(주소 값 비교)을 수행하기 때문이다.

public class ObjectExample {
    private final String str;

    public ObjectExample(final String str) {
        this.str = str;
    }

    public static void main(String[] args) {
        final ObjectExample s1 = new ObjectExample("aa");
        final ObjectExample s2 = new ObjectExample("aa");

        System.out.println(s1.equals(s2)); // false
    }
}

 

여태까지 사용해왔던 개념을 생각하면 이해가 되지 않을 것이다.

동일한 문자열을 비교했을 때 당연히 true가 나와야 하는데 false가 나오다니..

결과가 다른 이유는 equals() 메서드가 주소가 아닌 내용을 비교하도록 오버라이딩 되어있기 때문이다.

public class ObjectExample {
    public static void main(String[] args) {
        String s3 = "aa";
        String s4 = "aa";
        System.out.println(s3.equals(s4)); //true
    }
}

 

equals() 메소드도 마찬가지로 원본이 있다는 것을 잊지 말자!

 

 

*추가 꿀팁

매개변수로 받아온 문자열과 해당 메소드에서 생성한 문자열을 비교하고 싶은 경우, 순서가 중요하다.

간과하고 넘어갈 수 있는 부분이지만, str이 equals연산 앞에 오느냐 뒤에 오느냐에 따라 취약점이 발생한다.

만약 'str.equals(temp)' 방식으로 비교할 때 str이 null이라면 어떻게 될까?

바로 에러가 뜰 것이다.

그렇기 때문에 아래처럼 매개변수를 뒤쪽으로 빼놓는 것이 조금이라도 더 안전성이 높다.

public void method(final String str) {
    String temp = "abc";
    
    if (temp.equals(str)) {
    	~~
    }
}

 

 

 

hashCode()

- 기본 동작: heap에 저장된 객체의 메모리 주소를 반환.

 

해시코드란? : 객체를 식별할 수 있는 정수값

 

Object의 hashCode() 메소드는 객체의 메모리 번지를 이용해 해시코드를 만들어 반환해 객체마다 다른 값을 가진다.

주로 HashTable, HashSet, HashMap과 같은 자료구조에 사용된다.

 

두 객체가 동등한지 비교하는 과정에서 hashCode()와 equals() 메소드 2 과정을 거친다.

1. hashCode() 메소드를 실행해 해시코드 값이 같은지 본다. 다르면 다른 객체, 같으면 equals() 연산을 수행한다.

2. equals() 메소드로 다시 비교한다. 주소 값도 같다면 두 객체가 동등한 것이다.

 

위 과정을 거치기 때문에 일반적으로 두 메소드는 함께 오버라이딩한다.

 

예시를 살펴보자.

아래는 equals() 메소드만 재정의해 hashCode() 메소드를 실행한 경우이다.

해시코드값이 서로 다른 것을 확인할 수 있다. 그렇기 때문에 동일성을 확인하기 어렵다.

public class ObjectExample {
    private final String str;
    private final int id;

    public ObjectExample(final String str, final int id) {
        this.str = str;
        this.id = id;
    }

    public int getId() {
        return id;
    }

    @Override
    public boolean equals(final Object obj) {

        if (obj instanceof ObjectExample) {
            return this.getId() == ((ObjectExample)obj).getId();
        } else {
            return false;
        }
    }

    public static void main(String[] args) {
        final ObjectExample s5 = new ObjectExample("abc", 111);
        final ObjectExample s6 = new ObjectExample("abc", 111);

        System.out.println(s5.equals(s6)); //true
        System.out.println(s5.equals(null)); //false
        System.out.println(s5.hashCode()); //653305407
        System.out.println(s6.hashCode()); //1130478920
    }
}

 

* 추가로 위에서 equals 메소드에 null을 보냈을 때 반환값은 false다. 그 이유는 무엇 때문일까?

먼저 공식 문서를 참고해 equals() 메소드에 대해서 살펴보자.

참조) https://docs.oracle.com/javase/8/docs/api/index.html

위에서 3번째 줄을 보면 'The equals method implements an equivalence relation on non-null object references:'

: 'equals 메서드는 null이 아닌 개체 참조에 대해 동등성 관계를 구현합니다.' 라고 적혀있다.

그렇기 때문에 null은 equals() 연산의 비교 대상 자체가 될 수 없다.

그리고 위 코드 관점에서 살펴보면 null은 ObjectExample 클래스의 인스턴스로 선언되지 않았기 때문에 else 구문으로 빠져 false가 반환된다.

 

 

id를 사용해 hashCode()도 재정의를 해보자.

반환값이 동일한 것을 확인해 동일성을 구현했다고 볼 수 있다.

public static void main(String[] args) {
    final ObjectExample s5 = new ObjectExample("abc", 111);
    final ObjectExample s6 = new ObjectExample("abc", 111);

    System.out.println(s5.equals(s6)); //true
    System.out.println(s5.hashCode()); //111
    System.out.println(s6.hashCode()); //111
}

 

 

 

 

참조

http://tcpschool.com/java/java_api_object

https://atoz-develop.tistory.com/entry/%EC%9E%90%EB%B0%94-Object-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%A0%95%EB%A6%AC-toString-equals-hashCode-clone

https://kephilab.tistory.com/92

 

https://www.appletong.com/16

https://kmj1107.tistory.com/entry/JAVA-%EB%AC%B8%EC%9E%90%EC%97%B4string-%EB%B9%84%EA%B5%90-equals%EC%99%80-%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90-equals%EC%9D%98-%EB%B0%98%EB%8C%80

 

https://jisooo.tistory.com/entry/java-hashcode%EC%99%80-equals-%EB%A9%94%EC%84%9C%EB%93%9C%EB%8A%94-%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B3%A0-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C

https://atoz-develop.tistory.com/entry/%EC%9E%90%EB%B0%94-Object-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%A0%95%EB%A6%AC-toString-equals-hashCode-clone

 

 

 

반응형

'Java' 카테고리의 다른 글

[Java] 디미터의 원칙  (0) 2021.09.21
[Java] 인터페이스  (0) 2021.08.07
[Java] StringBuilder vs StringBuffer  (0) 2021.08.03
[Java] Solid란?  (0) 2021.07.28
[Java] 다형성, 캐스팅, 추상 클래스, 추상 메서드  (0) 2021.06.22