Giken Dev
article thumbnail
반응형

 

Object 클래스란?

모든 클래스는 Object 클래스의 자손 클래스이다.

public class SomeObject {
}

public class SombeObject extends Object {
}

 

따라서, extends Object 가 생략되어 있는거라고 생각하면 된다.

 

 

 

 

Object 클래스의 주요 메서드들

 

clone(), equals(), finalize(), getClass(), hashCode(), notify(), notifyAll(), toString(), wait()

 

이 중에서도 특히 자주 사용하는 세 가지 메서드를 알아보려한다

-> equals(), hashCode(), toString()

 

 

 

 

equals()

  • 동일성 : 비교 대상이 실제로 '똑같은' 대상이어야 함 ( = 둘은 실제로는 하나임)
    • 두 비교 대상을 '==' 연산자로 비교
  • 동등성 : 비교 대상이 같은 값이라고 우리가 정의한 것
    • equal() 메서드를 오버라이딩하여 비교

 

public class SomeObject {

    private int intField;
    private String stringField;

    public SomeObject(int intField, String stringField) {
        this.intField = intField;
        this.stringField = stringField;
    }

    public int getIntField() {
        return intField;
    }

    public String getStringField() {
        return stringField;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        SomeObject that = (SomeObject) o;
        return intField == that.intField && Objects.equals(stringField, that.stringField);
    }
}

override 한 equals() 는 인텔리제이에서 제공하는 Generate 옵션으로 만든 것이다.

 

여기서 무심코 썼던 equals() 의 상세한 동작 원리를 알 수 있겠다.
int 필드 하나와, Stirng 필드 하나가 있는 두 Object 가 완전히 같은 인스턴스인지 판단하고 있다.

 

 

 

public class EqualsExampleMain {
    public static void main(String[] args) {
    
        SomeObject sameObject1 = new SomeObject(1, "programmers");
        SomeObject sameObject2 = new SomeObject(1, "programmers");

        SomeObject anotherObject = new SomeObject(100, "foo");

        // 동일성 비교 -> false
        System.out.println(sameObject1 == sameObject2);

        // 동등성 비교 -> true
        System.out.println(sameObject1.equals(sameObject2));

        // 동등성 비교 -> false
        System.out.println(anotherObject.equals(sameObject1));
    }
}

 

 

출력 중에서 처음 두 줄은

실제로 똑같지는 않지만, 값이 같은 두 Object 인스턴스를 동일성, 동등성에서 비교하는 예제이다.

 

마지막 출력 줄은

실제로도 다르며, 값도 다른 두 Object 인스턴스를 동등성에서 비교하는 예제이다.

 

equals() 를 자주 사용하던 String 두 객체를 비교할때는, override 없이 그대로 사용하면 됐다.

허나 지금처럼 필드가 두 개 존재하는 class 를 새로 만들어서 두 객체를 비교할 때는 override 가 필요하다 ! 

-> 마치 Comparable interface 를 상속받아서 정렬을 위한 우선순위를 만들어주는 것처럼 (아래 예제)

 

 

// Comparable 인터페이스를 구현하는 Person 클래스
class Person implements Comparable<Person> {

    publice String name;

    public Person(String name) {
        this.name = name;
    }

    // compareTo 메서드를 구현하여 이름을 기준으로 오름차순 정렬
    @Override
    public int compareTo(Person otherPerson) {
        return this.name.compareTo(otherPerson.name);
    }

    @Override
    public String toString() {
        return name;
    }
}

 

 

 

 

 

 

hashCode()

equals() 와 마찬가지로 두 인스턴스가 같다는것을 비교하기위해 override 해서 쓴다고 한다.

다만 차이점은, 이 hashCdoe() 는 두 인스턴스가 hashmap 일 때 쓴다고한다.

 

 

작은 수(예를 들면 16)로 나머지한 값을 key 값을 만들기 때문에 key 값이 얼마든지 중복될 수 있다.

따라서 hashCode() 로 같은지 1차 선별을 한 후,

equals() 로 같은지 2차 선별을 하는 것이다.

 

 

그러면 처음부터 equals() 로 비교하면 될 텐데, 왜 hashCode() 로 먼저 비교하나?

-> 그 이유는 equals() 보다 상대적으로 가벼운 연산의 hahCode()로 1차 선별을 해서 찾으려는 값의 후보군을 대폭 줄일 수 있다 !

 

 

public class SomeObject {
    private int intField;
    private String stringField;

    public SomeObject(int intField, String stringField) {
        this.intField = intField;
        this.stringField = stringField;
    }

    public int getIntField() {
        return intField;
    }

    public String getStringField() {
        return stringField;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        SomeObject that = (SomeObject) o;
        return intField == that.intField && Objects.equals(stringField, that.stringField);
    }

//    hashCode가 오버라이딩 되고 안되고의 차이
//    @Override
//    public int hashCode() {
//        return Objects.hash(intField, stringField);
//    }
}

 

 

 

public class HashCodeExampleMain {
    public static void main(String[] args) {
    
        // Hash 관련 컬렉션이 같은 인스턴스를 구분하는 방법
        // hashCode 비교 -> equals 비교
        
        SomeObject sameObject1 = new SomeObject(1, "programmers");
        SomeObject sameObject2 = new SomeObject(1, "programmers");

        System.out.println(sameObject1.hashCode());
        System.out.println(sameObject2.hashCode());

        Set<SomeObject> set = new HashSet<>();

        set.add(sameObject1);
        set.add(sameObject2);

        System.out.println(set.size());
    }
}

 

hashCode() override 한 것을 주석처리하고 main 을 실행하면 

sameObject1 과 sameObeject2 각각은 다른 hashCode() 값이 나오게 된다.

따라서 이 둘은 다른 인스턴스로 판별되어서 set.size() 도 2가 된다!

 

 

//    @Override
//    public boolean equals(Object o) {
//        if (this == o) return true;
//        if (o == null || getClass() != o.getClass()) return false;
//        SomeObject that = (SomeObject) o;
//        return intField == that.intField && Objects.equals(stringField, that.stringField);
//    }

    @Override
    public int hashCode() {
        return Objects.hash(intField, stringField);
    }

 

여기서 만약 hashCode() 는 주석해제하고, equals() 를 주석처리하면?

sameObject1 과 sameObeject2 는 같은 hashCode() 값이 나오지만 equals() 에서 다른 값이 나와서 같지 않다고 판별된다.

따라서 이 경우에도, 둘은 다른 인스턴스로 판별되어서 set.size() 도 2가 된다!

 

 

 

 

 

 

toString()

: 인스턴스를 문자열로 만드는 것 (log 를 남기기 위해)

 

 

public class SomeObject {
    private int intField;
    private String stringField;

    public SomeObject(int intField, String stringField) {
        this.intField = intField;
        this.stringField = stringField;
    }

    @Override
    public String toString() {
        return "SomeObject{" +
                "intField=" + intField +
                ", stringField='" + stringField + '\'' +
                '}';
    }
}

 

 

public class ToStringExampleMain {
    public static void main(String[] args) {
        SomeObject someObject1 = new SomeObject(1, "programmers");
        SomeObject someObject2 = new SomeObject(100, "foo");

        System.out.println(someObject1);
        System.out.println(someObject2);
    }
}

 

// 출력 결과
// SomeObject{intField=1, stringField='programmers'}
// SomeObject{intField=100, stringField='foo'}

 

 

toString() 은 상대적으로 간단한 메서드라서

'log 를 찍을 때 사용하고, 출력 형식을 마음대로 만들 수 있다 ~ ' 이정도로 이해하고 넘어가면 되겠습니다.

반응형
profile

Giken Dev

@기켄

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!