[Java] Object 클래스와 주요 메서드 - equals(), hashCode(), toString()

2023. 9. 22. 12:15·Programming Language/Java
반응형

 

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

반응형
저작자표시 비영리 변경금지 (새창열림)

'Programming Language > Java' 카테고리의 다른 글

[Java] if문이 너무 많다면, 리팩토링을 해보자.  (0) 2023.09.25
[Java] Optional - nullPointException을 잘 다뤄보자.  (0) 2023.09.25
[Java] Checked & Unchecked exception 의 차이에 대하여  (0) 2023.09.22
[Java] Enum을 예제로 알아보자.  (0) 2023.09.22
[Java] 추상클래스(abstract class)와 인터페이스(interface)의 차이점과 예제  (0) 2023.09.22
'Programming Language/Java' 카테고리의 다른 글
  • [Java] if문이 너무 많다면, 리팩토링을 해보자.
  • [Java] Optional - nullPointException을 잘 다뤄보자.
  • [Java] Checked & Unchecked exception 의 차이에 대하여
  • [Java] Enum을 예제로 알아보자.
Giken
Giken
𝐒𝐲𝐬𝐭𝐞𝐦.𝐨𝐮𝐭.𝐩𝐫𝐢𝐧𝐭𝐥𝐧("𝐇𝐞𝐥𝐥𝐨 𝐖𝐨𝐫𝐥𝐝!");
  • Giken
    개발자 기켄
    Giken
  • 전체
    오늘
    어제
    • 분류 전체보기 (148)
      • Programming Language (26)
        • C (3)
        • C++ (2)
        • Java (19)
      • Web (4)
      • Database (1)
        • SQL (5)
      • Spring (10)
      • PHP (7)
      • Linux (1)
      • Server (1)
      • Infra (3)
      • Algorithm (74)
        • 백준 (71)
        • 프로그래머스 (0)
      • 프로젝트 (2)
      • Etc (8)
      • 낙서 (5)
  • 블로그 메뉴

    • GitHub
  • 링크

    • GitHub
  • 공지사항

  • 인기 글

  • 태그

    윤년
    백준
    2753
    프로그래머스
    DB
    9498
    2588
    평년
    SQL고득점키트
    C
    1330
    SQL
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Giken
[Java] Object 클래스와 주요 메서드 - equals(), hashCode(), toString()
상단으로

티스토리툴바