2 minute read

toString()이란

  • 객체를 문자열로 표현하는 값을 반환하는 메서드이다.
  • Object 클래스에 정의되어 있다.
  • 기본 반환값은 “객체의 타입인 클래스의 이름 + @ + 객체의 해시 코드를 표현한 값(서명되지 않은 16진수로 표현)”이다.
Person@15db9742

왜 오버라이딩 할까?

  • 객체가 자신을 사람이 읽을 수 있는 형태로 표현하는 공식적인 방법 중 유일한 수단이다.
    • 개인적인 의견으로는 이게 toString()을 오버라이딩하는 가장 중요한 이유라고 생각한다. 개발자들은 사람이니까.
    • 모든 클래스들이 상속하는 Object 클래스에서 객체가 자신을 사람이 읽을 수 있는 형태로 표현하도록 하는 메서드는 toString()이 유일하다.
    • toString()이 없으면 커스텀 메서드를 만들어야 한다.
      // 개발자: 뭐라는거야?
      User@1a2b3c
      // 개발자: 이해 완료!
      User[id=101, name=John, role=ADMIN]
    
  • 객체의 상태를 나타낼 수 있는 수단이 된다.
    • 객체의 상태(값이 할당된 필드)는 객체의 근본이기 때문에 매우 중요하다.
      • 객체는 자신의 필드와 그 값에 의해 정의되기 때문이다.
      • Account는 잔액, 계좌번호, 소유자로 결정된다.
      • Car는 생산업체, 색, 속도, 연비로 결정된다.
      • Person은 이름, 나이, 주소로 결정된다.
    • Java의 Record가 hashcode(), equals() 뿐 아니라 toString()도 자동적으로 오버라이딩 해주는 의미에 대해 생각해보자.
    • 그리고 오버라이딩해주는 문자열의 내용이 왜 필드의 이름과 값의 쌍일지 생각해보자.
      // User 레코드의 toString() 값 예시
      User[id=101, name=John, role=ADMIN]
    
  • 유의미한 정보를 디버깅 및 로깅에 사용할 수 있다.
    • 특히 프로덕션 환경에서 문제가 발생했을 때 유용하다.
      • 운영중인 프로덕션 환경은 디버거(debugger)를 적용할 수 없다. 이 때 문제 상황에 대한 정보를 알려줄 수 있는 건 로깅이다.
      • 로깅에 사용되는 메서드가 toString()이다. toString()은 객체가 스스로를 문자열로 표현하는 수단이기 때문이다.
      // 모든 Java의 Exception들이 상속하는 Throwable도
      // 자신만의 toString()이 있음을 확인할 수 있다.
      public String toString() {
          String s = getClass().getName();
          String message = getLocalizedMessage();
          return (message != null) ? (s + ": " + message) : s;
      }
    
    • toString()의 기본적 반환값인 “클래스 이름 + @ + 해시코드 표상”은 개발자에게 어떠한 정보도 제공하지 못한다.
      // 테스트를 실패했는데 이런 메시지가 뜬다면
      // 뭐 어쩌라고? 라는 생각이 들 것이다.
      // 필드의 값이 달랐다던지, 예상한 값이 아니었다던지 등을 보여야지.
      Expected: User@1a2b3c but was: User@4d5e6f
    
  • 외부 서비스와 상호작용할 때 객체를 전달할 수 있는 수단이 된다.
    • 문자열은 모든 프로그램 및 프로그래밍 언어가 처리할 수 있는 자료이다.
    • 즉, 자바 애플리케이션에서 객체를 외부로 내보내기 위해 문자열로 바꿔야 하는 순간이 언젠가는 온다.
    • 이 때 객체에 대한 정보를 가져오기 위해 toString()을 사용할 수 있다.

➕ 양방향적 관계에 있는 클래스에서 toString()을 오버라이딩할 때 주의해야 하는 이유

  • 클래스에 있어 양방향적 관계란 서로가 서로를 참조하는 관계를 의미한다.
  • 서로를 참조하기 때문에 무한 재귀 호출의 문제가 발생하여 StackOverflowError가 발생한다.
  • 발생 과정
    • ClassA와 ClassB가 서로를 참조한다.
    • ClassA의 toString()을 부른다.
    • ClassA의 toString()은 ClassB의 toString()이 필요하다.
    • ClassB의 toString() 역시 ClassA의 toString()이 필요하다.
    • 서로가 서로를 호출하다가 StackOverflowError가 발생한다.
  • toString()을 오버라이딩할 때 무한 재귀 호출을 일으키지 않는 필드만 사용하거나, id 필드만 사용하는 등의 방법으로 방지한다.
  • 알아보고 난 뒤 내가 내린 결론: 양방향적 관계에 있는 클래스에서 toString()을 오버라이딩하는 게 문제가 아니라, 클래스들이 양방향적 관계에 있는 것 자체가 문제다.

Categories:

Updated: