ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [이펙티브 자바] 아이템12. toString을 항상 재정의하라
    기타/북스터디 2023. 1. 2. 22:00

    toString메서드는 Object클래스 정의된 public method이다.

    간단히 toString이 어떠한 값을 반환하는지보면 클래스의 이름@hascode를 16진수로 변환한 값을 반환해주고 있다.

     

    실제로 코드를 작성하여 toString을 실행시켜보면 아래와 같은 결과가 나오는 것을 알 수 있다.

    package Item12;
    
    // 모든 클래스는 직접 extends를 통해 Object클래스 상속을 선언해주지 않아도
    // 기본적으로 Object 클래스를 상속한다.
    public class Foo {
    
      private String name;
      private int code;
    
      public Foo(String name, int code) {
        this.name = name;
        this.code = code;
      }
      
      public static void main(String[] args) {
        Foo foo = new Foo("name", 1);
        // 인스턴스를 출력하면 자동으로 toString메서드에서 반환한 값을 출력해주기 때문에
        // toString메서드는 명시적으로 호출해주지 않아도 된다.
        System.out.println(foo.toString());
      }
    }

     

    이펙티브 자바의 내용에 따르면 toString의 일반 규약에 따르면 '간결하면서 사람이 읽기 쉬운 형태의 유익한 정보'를 반환해야한다고 한다.

    (Object#toString의 주석에 보면 참고할 수 있다.)

     

    API Note에서는 "It is recommended that all subclasses override this method." 즉, 모든 서브클래스에서 toString메서드를 재정의하는 것을 권장하고 있고 이에 대해 Item12에서는 toString 재정의를 통해 훨씬 유익한 정보를 주도록 해주는 것을 권장하고 있다.

     

    그러면 toString메서드는 언제 활용될까?

    아래의 경우에 toString은 자동으로 호출된다.

    1. 객체를 println, printf, 문자열 연결 연산자(+), assert구문에 넘길 때

    2. 디버거가 객체를 출력할 때

    3. 우리가 만든 객체를 참조하는 컴포넌트가 오류 메시지를 로깅할 때

     

    결국에는 toString이 묵시적으로 호출되어졌을 때에 기본적으로 반환되는 문자로는 유익한 정보를 알기 힘드므로 재정의를 하여 우리가 해당 인스턴스가 어떠한 인스턴스인지 알 수 있는 유익한 정보를 반환해주도록 하는 것이 목적이다.

     

     

    "실전에서 toString은 그 객체가 가진 주요 정보 모두를 반환하는게 좋다?"

    책에 나온 예제인 PhoneNumber라는 객체를 생각했을 때에는 저자가 말한 의견은 공감이 된다.

    하지만, 모든 클래스에 대해 toString이 주요 정보 모두를 반환하는 것이 좋다고는 생각이 들지 않는다.

    가령, User라는 객체를 생각해보면 User가 가진 주요 정보는 이름, 생년월일, 사는 곳 등일텐데 이러한 개인정보를 모두 반환하는 것이 올바른가에 대해 생각해봤을 때에 나는 아니라고 생각한다.

    따라서, toString을 통해 제공가능한 선에서 해당 객체가 어떠한 객체인지 알려줄 유익한 정보를 반환해주는게 좋을 것같다.

     

     

    "toString을 구현할 때면 반환값의 포맷을 문서화할지 정해야 한다."

    여기서 저자가 말한 문서화는 따로 한글파일이나 노션으로 정리하는 문서화보다는 아래와 같이 JavaDoc을 이용하여 주석으로 남기는 것을 의미하는 것같다.

    // 출처: 이펙티브자바 p.75코드
    /**
    * 이 전화번호의 문자열 표현을 반환한다.
    * 이 문자열은 "XXX-YYY-ZZZZ"형태의 12자로 구성된다.
    * XXX는 지역코드, YYY는 프리픽스, ZZZZ는 가입자 번호다.
    * 각각의 대문자는 10진수 숫자 하나를 나타낸다.
    *
    * 전화번호의 각 부분의 값이 너무 작아서 자릿수를 채울 수 없다면,
    * 앞에서부터 0으로 채워나간다.
    * ex) 가입자 업노하 123이면 전화번호의 마지막 네 문자는 0123
    */
    @Override
    public String toString() {
      return String.format("%03d-%03d-%04d", ..., ..., ...);
    }

    실제 자바에서 제공하는 ZonedDateTime 클래스의 toString메서드에서도 아래와 같이 문서화가 되어있는 것을 볼 수 있다.

     

    추가적으로 저자는 아래와 같은 의견을 남겼다.

    "포맷을 명시하기로 했다면, 명시한 포맷에 맞는 문자열과 객체를 상호 전화할 수 있는 정적 팩토리나 생성자를 함께 제공해주면 좋다."

    즉, 아래와 같이 정적팩토리를 제공하는 것을 제안한 것인데 개인적인 생각으로는 굳이..?라는 생각이 들고 포맷이 바뀐다면 팩터리메서드도 바껴야하는 단점이 있기때문에 좋은 것인지는 잘 모르겠다.

    public class PhoneNumber {
    
        private int areaCode;
        private int prefix;
        private int lineNum;
        
        public static PhoneNumber of(String formattedPhoneNumber) {
        	// 1. 인자인 formmattedNumber를 파싱
            String[] splits = formmatedPhoneNumber.split("-");
            // 2. 파싱된 것들을 생성자의 파라미터로 넘겨 인스턴스 반환
            return new PhoneNumber(Integer.parseInt(splits[0]),
                                   Integer.parseInt(splits[1]),
                                   Integer.parseInt(splits[2]));
        }
    }

     

    이렇게 포맷을 명시하는 것에 대한 단점도 책에서 언급을 해주고 있다.

    한마디로 정리하면 규격화된 포맷으로 인해 변경에 취약하다는 것이다.

     

     

    "toString이 반환한 값에 포함된 정보를 얻어올 수 있는 API를 제공하자."

    해당 내용은 toString으로 제공하는 클래스의 멤버변수에 대해 멤버변수를 반환하는 접근자를 제공해줘야한다는 것이다.

    간단히 말하면, getter를 제공해야한다는 것이다.

     

     

    "toString을 재정의하지 않아도 되는 경우도 있다."

    1. 정적 유틸클래스인 경우는 toString을 제공할 이유가 없다.

    -> 내 생각에는 정적 유틸 클래스는 정적변수, 정적 메서드와 같은 멤버만 가지는 클래스이므로 static method가 아닌 toString을 제공할 필요가 없다는 것으로 이해했다.

     

    2. 열거 타입은 이미 완벽한 toString을 제공하므로 따로 재정의 하지 않아도 된다.

    댓글

Designed by Tistory.