toString
메서드가 우리가 작성한 클래스에 적합한 문자열을 반환하는 경우는 거의 없음, 단순히 클래스_이름@16진수로_표시한_해시코드
를 반환할 뿐임
해당 메서드에 일반 규약은 간결하면서 사람이 읽기 쉬운 형태의 유익한 정보를 반환해야함, 모든 하위 클래스에서 이 메서드를 재정의해야하는 규약도 있음
실전에서 toString
은 그 객체가 가진 주요 정보 모두를 반환하는게 좋음
객체가 거대하거나 객체의 상태가 문자열로 표현하기에 적합하지 않다면 무리가 있음, 그런 상황에선 요약 정보를 담는게 좋음
이상적으로는 스스로 완벽히 설명하는 문자열이어야함
toString
을 구현할 때 반환값의 포맷을 문서화할 지 정해야함
전화번호나 행렬 값 같은 클래스라면 문서화하는게 나음
포맷을 명시하면 그 객체는 표준적이고, 명확하고, 사람이 읽을 수 있게됨
그 값 그대로 입출력에 사용하거나 CSV 파일처럼 사람이 읽을 수 있는 데이터 객체로 저장할 수도 있음
포맷을 명시하기로 했다면 명시한 포맷에 맞는 문자열과 객체를 상호 전환할 수 있는 정적 팩터리나 생성자를 함께 제공해주면 좋음
단 포맷을 한 번 명시하면 평생 그 포맷에 얽매이게 됨, 그 포맷에 맞게 파싱하고 만들어야 하고 그렇다고 이를 바꾸면 이전 코드에 영향을 끼침
포맷을 명시하지 않으면 이런 부분의 유연성이 생기긴 함, 어찌됐든 포맷을 명시하든 안하든 의도는 명확히 밝혀야함(아주 정확하게)
예를 들어서 포맷을 명시하면 아래와 같이 쓸 수 있음
/**
* 이 전화번호의 문자열 표현을 반환한다.
* 이 문자열은 "XXX-YYY-ZZZZ" 형태의 12글자로 구성된다.
* XXX는 지역 코드, YYY는 프리픽스, ZZZZ는 가입자 번호다.
* 각각의 대문자는 10진수 숫자 하나를 나타낸다.
*
* 전화번호의 각 부분의 값이 너무 작아서 자릿수를 채울 수 없다면,
* 앞에서부터 0으로 채워나간다. 예컨대 가입자 번호가 123이라면
* 전화번호의 마지막 네 문자는 "0123"이 된다.
*/
@Override public String toString() {
return String.format("%03d-%03d-%04d", areaCode, prefix, lineNum);
}
/**
* 이 약물에 관한 대략적인 설명을 반환한다.
* 다음은 이 설명의 일반적인 형태이나,
* 상세 형식은 정해지지 않았으며 향후 변경될 수 있다.
*
* "[약물 #9: 유형=사랑, 냄새=테레빈유, 겉모습=먹물]"
*/
@Override public String toString() { ... }
결과적으로 위처럼 의도를 명확히 밝혀야 함을 의미함
포맷 명시 여부와 상관없이 toString
이 반환한 값에 포함된 정보를 얻어올 수 있는 API를 제공해야함
위의 예시에서도 areaCode
,prefix
,linNum
접근자를 제공해야함, 그렇지 않으면 파싱을 해야하는데 파싱하는 방식은 성능이 나빠지고 포맷을 바꾸면 시스템이 망가지는 문제가 있음
정적 유틸리티 클래스는 toString
을 제공할 이유가 없음, 또한 대부분의 열거 타입도 자바가 이미 완벽한 toString
을 제공하니 따로 재정의하지 않아도 됨
하지만 하위 클래스들이 공유해야 할 문자열 표현이 있는 추상 클래스라면 toString
을 재정의 해줘야함, 대다수 컬렉션 구현체는 추상 컬렉션 클래스들의 toString
메서드를 상속해서 씀
AutoValue 프레임워크는 toString
도 생성해줌