값 객체가 되었으니 이제 값으로 비교할 수 있어야 한다. new Dollar(5).times(2)와 new Dollar(10)은 서로 다른 인스턴스지만 같은 값 $10이다. 자바의 기본 equals()는 참조를 비교하므로 이 둘을 다르다고 본다. 값 객체라면 값이 같을 때 같다고 답해야 한다.
삼각측량 — 예제 두 개로 일반화를 강제한다
작은 테스트 하나만으로는 return true라고 거짓말할 수 있다. 벡은 이럴 때 삼각측량(Triangulation)을 쓴다. 서로 다른 두 예제를 놓고, 둘 다 통과시키려면 어쩔 수 없이 진짜 로직을 쓸 수밖에 없게 만든다. 하나의 예제로는 방향만, 두 개의 예제로는 지점을 정한다.
@Testpublic void testEquality() { assertTrue(new Dollar(5).equals(new Dollar(5)));}class Dollar { int amount; Dollar(int amount) { this.amount = amount; } Dollar times(int multiplier) { return new Dollar(amount * multiplier); } // equals()가 없어 Object의 참조 비교가 쓰인다 → 실패}
@Testpublic void testEquality() { assertTrue(new Dollar(5).equals(new Dollar(5)));}class Dollar { int amount; Dollar(int amount) { this.amount = amount; } Dollar times(int multiplier) { return new Dollar(amount * multiplier); } // equals()가 없어 Object의 참조 비교가 쓰인다 → 실패}
@Testpublic void testEquality() { assertTrue(new Dollar(5).equals(new Dollar(5))); assertFalse(new Dollar(5).equals(new Dollar(6))); // 두 번째 예제}class Dollar { int amount; Dollar(int amount) { this.amount = amount; } Dollar times(int multiplier) { return new Dollar(amount * multiplier); } public boolean equals(Object object) { return true; // 두 번째 예제가 이 거짓말을 깬다 }}
@Testpublic void testEquality() { assertTrue(new Dollar(5).equals(new Dollar(5))); assertFalse(new Dollar(5).equals(new Dollar(6)));}class Dollar { int amount; Dollar(int amount) { this.amount = amount; } Dollar times(int multiplier) { return new Dollar(amount * multiplier); } public boolean equals(Object object) { Dollar dollar = (Dollar) object; return amount == dollar.amount; // amount의 중복 비교가 일반 해다 }}
assertTrue 하나만 있을 때는 return true로도 초록이 된다. 하지만 assertFalse(...equals(new Dollar(6)))를 더하는 순간 그 거짓말이 무너지고, amount를 실제로 비교하는 수밖에 없어진다. 두 예제가 정답의 위치를 삼각으로 좁혀 준다.
판단 기준: 구현이 명백하면 곧장 쓰고, 어떻게 일반화할지 감이 안 오면 삼각측량으로 예제를 하나 더 놓는다. 함정: 삼각측량은 강력하지만 느리다. 일반 해가 눈에 보이는데도 예제를 두 개씩 까는 건 낭비다 — 확신이 없을 때만 쓰는 도구다. 또 하나, 지금 equals는 null과 다른 타입을 무시한다. 완벽하지 않지만 목록에 적고 넘어간다.
이 장이 남긴 할 일 목록
equals()- amount를 public으로 노출 중 → equals가 생겼으니 개선할 수 있다 → 4장
equals(null),equals(다른 객체)처리hashCode()- 10 (환율 2:1)
다음장으로 4장