ITEM 12 "toString 을 항상 재정의하라"
이번 장에서는 Object 의 toString 을 항상 재정의하라고 권고하고 있다.
equals 와 hashCode 규약처럼 중요하진 않지만, toString 을 잘 재정의한 클래스는 사용하기 편하고 디버깅하기 쉽다.
기본 toString 메서드는 클래스_이름@16진수로_표시한_해시코드 만 반환하는데 이는 사람이 읽기 어려울 뿐 아니라 쓸모 없는 메세지만 로그에 남을 것이다.
가급적 그 객체가 가진 주요 정보 모두를 반환하는게 좋다. 객체가 크거나 문자열로 표현하기에 적합하지 않다면 요약 정보로 표현해도 된다.
또 반환값의 포맷을 결정하고 문서화하면 좋다. 포맷을 명시하면 표준적이고 명확하며 사람이 읽을 수 있게 된다. 추가로 toString 포맷으로 생성할 수 있는 생성자나 정적 팩토리 메서드를 같이 정의하면 금상첨화다.
BigInteger 클래스에서 제공하는 toString 반환 포맷으로 생성자를 호출할 수 있게 구성되어 있는 것을 볼 수 있다.
하지만, 한 번 포맷을 명시하면 평생 그 포맷에 얽매이게 되어 다음 릴리스에서 포맷을 바꾸면 이를 사용하던 코드들 모두 변경해야 되는 단점이 있다. 이 반대 개념으로 포맷을 명시하지 않는다면 포맷에 얽매이지 않고 프로그래밍할 수 있다. 향후 릴리스에서 추가로 정보를 넣거나 포맷을 개선할 수 있는 유연함을 가지게 된다. 어떠한 방법이던 포맷을 명시하든 아니든 의도는 분명히 알 수 있도록 주석은 달아두자.
책에서는 포맷 명시 여부와 상관없이 toString 반환 값에 포함된 정보를 얻어올 수 있는 API 를 제공해야 한다고 하는데 나는 이 API 가 getter 메서드를 말하는 것 같았다. 프로그래머가 직접 파싱해서 정보를 얻어오는 폐해를 피하고자 단순히 필드 값들을 제공해주면 되니깐 말이다.
public class EffectiveJavaTest {
@Test
public void 전화번호_테스트() {
PhoneNumber testPhone = new PhoneNumber("010", "1234", "5678");
System.out.println(testPhone);
PhoneNumber testPhone2 = PhoneNumber.createByString(testPhone.toString());
System.out.println(testPhone2);
}
@Builder
@Getter
public static class PhoneNumber {
protected String firstNumber;
protected String secondNumber;
protected String thirdNumber;
private static final Pattern phoneNumberPattern = Pattern.compile("^\\d{3}-\\d{4}-\\d{4}$");
public PhoneNumber(String firstNumber, String secondNumber, String thirdNumber) {
this.firstNumber = firstNumber;
this.secondNumber = secondNumber;
this.thirdNumber = thirdNumber;
}
/*
* toString 메서드는 전화번호의 문자열 표현을 반환한다.
* 010-xxxx-yyyy 형태의 11글자로 구성된다.
* 전화번호의 각 부분 값이 너무 작아서 자릿수를 채울 수 없다면,
* 앞에서부터 0을 채워나간다. 예컨데 중간 번호가 123이라면, 0123 으로 표기한다.
* */
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(firstNumber);
sb.append("-");
sb.append(secondNumber);
sb.append("-");
sb.append(thirdNumber);
return sb.toString();
}
public static PhoneNumber createByString(String phoneNumber) {
if(!phoneNumberPattern.matcher(phoneNumber).find()) {
throw new IllegalArgumentException(phoneNumber + " cannot be parsed");
}
String[] numbers = phoneNumber.split("-");
return PhoneNumber.builder()
.firstNumber(numbers[0])
.secondNumber(numbers[1])
.thirdNumber(numbers[2])
.build();
}
}
}
책에서 권장하는 방법들을 모두 적용해 보았다.
"toString 을 항상 재정의하고 문서화하자
toString 반환 값으로 생성할 수 있는 생성자도 마련하자"
'독후감 > Effective JAVA' 카테고리의 다른 글
[Effective JAVA] 14 "Comparable 을 구현할지 고려하라" (0) | 2022.05.06 |
---|---|
[Effective JAVA] 13 "clone 재정의는 주의해서 진행하라" (0) | 2022.05.05 |
[Effective JAVA] 11 "equals를 재정의하려거든 hashCode도 재정의하라" (0) | 2022.05.03 |
[Effective JAVA] 10 "equals는 일반 규약을 지켜 재정의하라" (0) | 2022.04.30 |
[Effective JAVA] 9 "try-with-resource 를 사용하라" (0) | 2022.04.29 |