[CODE-STATES-BE] SEC-1[JAVA] 클래스 조상님 , Object : Equals 와 HashCode

유형찬·2022년 9월 5일
0

Code States

목록 보기
4/21

Object

상속

abstact class , class extend 등은 기본적인 내용이라 간단하게 설명 하고 넘어가고자 한다.

간단하게 설명 하자면 , 상속 할 클래스의 내용을 상속 받을 클래스가 그대로 쓸 수 있다. 그리고 기능 등을 커스터 마이징 할 수 있다는 점 ( Override )

Object

사실 모든 .class 는 object 라는 클래스를 상속 받는다.

상속 관계를 extend 로 명시 하지 않아도 object 클래스를 항상 컴파일 후에 자동으로 상속 해준다.

명시적으로 상속을 받더라도 상속을 하는 부모 객체에서 Object class 를 상속 받는다.

따라서 기본적으로 자바에서 객체들은 Object class 가 가지고 있는 함수들을 그대로 사용 할 수 있다.

사실 취업 시장에서 단골 질문이 아닐 수 가 없는 함수들이 아닐까 싶다. 본인 면접 기준으로 모든 면접에서 나온 질문

특히 Equals() 함수와 hashcode() 함수가 뭔지 제대로 알고 넘어가야 할 필요가 있다.

Function : equals()

boolean equals(Object obj)로 정의된 equals 메소드는 2개의 객체가 동일한지 검사하기 위해 사용된다. eqauls가 구현된 방법은 2개의 객체가 참조하는 것이 동일한지를 확인하는 것이다.

기본적으로 객체에서 override 를 하지 않는다면 ( == ) 과 equals 는 같다고 볼 수 있다.

//Object 에서 Equals 
public boolean equals(Object obj) {
    return (this == obj);
}
// String class 에서 equals 
public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }

    return false;
}

Primitive type 은 stack 에 값이 저장되고 메모리에 직접 값이 저장 되지만

Reference type 변수들은 stack에 Heap에 저장된 객체의 주소를 가져오게 된다.

여기서 간극이 발생하게 되는데 == 연산자의 경우 주소 비교 ( Stack에 쌓인 값 비교 ) 를 하게 된다.

그런데 Primitive type 은 메모리에 값이 저장 되어 있어서 값이 같을 경우 ( 1 == 1 ) true 이지만

Reference type 변수들은 Stack에 ( ae1221e457 ) 와 우리 눈에 16 진수로 보이는 메모리 주소 값이 저장되게 된다. 따라서 실제 객체의 인스턴스 변수가 같더라도 == 연산자는 false 가 나온다.

다음 예제를 통해 알아보자 .

다음 Employee Class 는 Id 값을 Primary Key 로 가진다.

public class Employee{
    private Integer id;
    private String firstname;
    private String lastName;
    private String department;
}

아래 예제는 당연하게도 Equals Override 를 하지 않았기 때문에 false 가 나온다.

그런데 우리는 Id 값으로 고유하게 객체를 가지고 싶다.

이런 경우에 equals 메소드를 오버라이드 해야 한다.

public static void main(String[] args) {
        Employee e1 = new Employee();
        Employee e2 = new Employee();

        e1.setId(100);
        e2.setId(100);

        System.out.println(e1.equals(e2));  //false
    }

아래와 같이 값 비교를 할 수 있는 로직을 추가 하여 Override 한다.

public boolean equals(Object object) {
    if(object == null) {
        return false;
    }
    if (object == this) {
        return true;
    }
    if (getClass() != object.getClass()) {
        return false;
    }

    Employee e = (Employee) object;
    return (this.getId() == e.getId());

}

위와 같이 원하는 결과물을 얻을 수 있다.

Function : Hashcode()

BUT

Equals() 함수를 위와 같이 Override 하고 hashcode() 함수를 Override 를 하지 않았다면

이제 Hash를 기반으로 하는 자료 구조들 HashMap , HashSet 등에서 문제가 생긴다.

hashcode() 함수는 객체의 주소를 반환 한다. equals 함수에서는 각각의 객체가 같다고 여겨지는데 Hash 자료 구조에서는 hashcode() 함수를 통해서 같은지 비교 하기 때문에 주소가 달라서 다르다고 여겨 진다.

다음 구조를 보자.

 public static void main(String[] args) {
        Employee e1 = new Employee();
        Employee e2 = new Employee();

        e1.setId(100);
        e2.setId(100);
				// equals 는 Override 한 상태 
				// hashcode는 Override 한 상태  X 
				Map<Employee , Integer> eNum = new HashMap<>;
				// e1 이 없으면 0 + 1 있으면 가져온 값에 + 1 
				eNum.put(e1,map.getOrDefault(e1 , 0) + 1);
				eNum.put(e2,map.getOrDefault(e2 , 0) + 1);
				        
    }

다음 구조를 쓸 일은 없겠지만 설명을 위해 쓰자면

위와 같은 구조에서 우리가 원하는 결과 값으로는 eNum의 Size 는 1이 나와야 한다.

왜? 같은 객체니까

그러나 결과는 size 는 2가 나오게 된다. 두 주소값이 다르기 때문이다.

이런 결과를 막기 위해서

@Override
public int hashCode() {
    final int PRIME = 31;
    int result = 1;
    result = PRIME * result + getId();
    return result;
}

다음과 같이 hashcode 를 Override 해야 한다.

profile
rocoli에요

0개의 댓글