자바 Obejct 클래스 equals(), hashCode(), toString()와 Record

용씨·2023년 2월 4일
0

자바 개념 시리즈

목록 보기
2/9

클래스를 선언할 때 extends 키워드로 다른 클래스를 상속하지 않으면 암시적으로 java.lang.Object 클래스를 상속하게 된다. 따라서 자바의 모든 클래스는 Object의 자식이거나 자손 클래스이다.

Object 클래스 메소드

  • boolean equals(Object obj) : 객체의 번지를 비교하고 결과를 리턴
  • int hashCode() : 객체의 해시코드를 리턴
  • String toString() : 객체의 문자 정보를 리턴

equals() 메소드

equals() 메소드는 비교 연산자인 ==과 동일한 결과를 리턴한다. 두 객체가 동일한 객체라면 true를 리턴하고, 그렇지 않으면 false를 리턴한다.

일반적으로 equals() 메소드는 재정의해서 동등 비교용으로 사용된다. 동등 비교란 객체가 비록 달라도 내부의 데이터가 같은지를 비교하는 것을 말한다. 예를 들어 String은 equals() 메소드를 재정의해서 내부 문자열이 같은지를 비교한다.

public boolean equals(Object anObject) {
      if (this == anObject) {
          return true;
      }
      if (anObject instanceof String) {
          String aString = (String)anObject;
          if (coder() == aString.coder()) {
              return isLatin1() ? StringLatin1.equals(value, aString.value)
                                : StringUTF16.equals(value, aString.value);
          }
      }
      return false;
  }

hashCode() 메소드

객체의 해시코드란 객체를 식별하는 정수를 말한다. hashCode() 메소드는 객체의 메모리 번지를 이용한 해시코드를 생성해서 정수값을 리턴한다.

일반적으로 hashCode() 메소드는 equals()와 마찬가지로 동등 비교용으로 사용된다. 객체의 데이터를 기준으로 재정의해서 새로운 정수값을 리턴한다. 객체가 다르다 할지라도 내부 데이터가 동일하다면 같은 정수값을 리턴하기 위해서이다.

자바는 두 객체가 동등함을 비교할 때 hashCode나 equals() 둘 중 하나만 쓰지 않는다. hashcode()가 리턴하는 정수값이 같은지를 확인하고, 그 다음 equals() 메소드가 true를 리턴하는지를 확인하는 2단계를 거쳐서 동등 객체임을 판단한다.

toString() 메소드

toString()는 객체를 문자열로 표현하는 메소드이다. '클래스명@16진수해시코드'로 구성된 문자열을 리턴한다. 객체의 문자 정보가 중요할 때 toString()을 재정의해서 사용하는데, 그 예로 Date 클래스와 String 클래스이다. Date 클래스는 현재 날짜와 시간을, String 클래스는 저장된 문자열을 리턴하도로 toString()을 오버라이딩했다.


레코드(Record)

데이터 전달을 위한 DTO(Data Transfer Object)를 작성할 떄 반복적으로 사용되는 코드를 줄이기 위해 Java 14부터 레코드(Record)가 도입되었다.

public class Person {
    private final String name;
    private final int age;


    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String name() {
        return this.name;
    }

    public int age() {
        return this.age;
    }

    @Override
    public int hashCode() {
        return super.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return super.equals(obj);
    }

    @Override
    public String toString() {
        return super.toString();
    }
}

위와 동일한 코드를 생성하는 레코드 선언이다.

public record Person(String name, int age) {

}

이렇게 선언된 레코드 소스를 컴파일하면 변수의 타입과 이름을 이용해서 private final 필드가 자동 생성되고, 생성자 및 Getter 메소드가 자동으로 추가된다. 그리고 hashCode(), equals(), toString() 메소드를 재정의한 코드도 자동으로 추가된다.

컴파일

Person.java를 컴파일 해서 Person.class 파일을 만들었다.

$ javac Person.java

디컴파일

class 파일의 내용을 확인하기 위해서 class 파일을 다시 java 파일로 바꿔주는 디컴파일 과정이 필요하다. 이를 위해 Java Decompiler의 JD-GUI를 사용했다.

참고로 자바의 javac 컴파일러 최적화 옵션이 켜져있다면 .java -> .class 컴파일 과정에서 소스가 바뀔 수 있기 때문에 디 컴파일하더라도 기존의 소스와는 조금 달라질 수 있다고 한다.

public final class Person extends Record {
  private final String name;
  
  private final int age;
  
  public Person(String paramString, int paramInt) {
    this.name = paramString;
    this.age = paramInt;
  }
  
  public final String toString() {
    // Byte code:
    //   0: aload_0
    //   1: <illegal opcode> toString : (LPerson;)Ljava/lang/String;
    //   6: areturn
    // Line number table:
    //   Java source line number -> byte code offset
    //   #1	-> 0
  }
  
  public final int hashCode() {
    // Byte code:
    //   0: aload_0
    //   1: <illegal opcode> hashCode : (LPerson;)I
    //   6: ireturn
    // Line number table:
    //   Java source line number -> byte code offset
    //   #1	-> 0
  }
  
  public final boolean equals(Object paramObject) {
    // Byte code:
    //   0: aload_0
    //   1: aload_1
    //   2: <illegal opcode> equals : (LPerson;Ljava/lang/Object;)Z
    //   7: ireturn
    // Line number table:
    //   Java source line number -> byte code offset
    //   #1	-> 0
  }
  
  public String name() {
    return this.name;
  }
  
  public int age() {
    return this.age;
  }
}
profile
아침형 인간이 목표

0개의 댓글