이펙티브 자바
의아이템 12. toString을 항상 재정의하라
를 읽으며 학습한 내용
public class Object {
/**
* Returns a string representation of the object. In general, the
* {@code toString} method returns a string that
* "textually represents" this object. The result should
* be a concise but informative representation that is easy for a
* person to read.
* It is recommended that all subclasses override this method.
* <p>
* The {@code toString} method for class {@code Object}
* returns a string consisting of the name of the class of which the
* object is an instance, the at-sign character `{@code @}', and
* the unsigned hexadecimal representation of the hash code of the
* object. In other words, this method returns a string equal to the
* value of:
* <blockquote>
* <pre>
* getClass().getName() + '@' + Integer.toHexString(hashCode())
* </pre></blockquote>
*
* @return a string representation of the object.
*/
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
}
클래스의 이름 + @ + 해시코드의 16진수
를 반환그냥 안쓰면 되는 거 아닐까?
System.out.println(obj);
public class PrintStream extends FilterOutputStream implements Appendable, Closeable {
// ...
public void print(Object obj) {
write(String.valueOf(obj));
}
// ...
}
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
// ...
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
// ...
}
클래스의 이름 + @ + 해시코드의 16진수
가 나오게 된다.public class Square {
private final File file;
private final Rank rank;
}
Square
는 File(행)
과 Rank(열)
로 체스판의 위치를 나타내는 클래스이다. @ParameterizedTest
@MethodSource("directionDummy")
@DisplayName("두 Square에 대한 방향을 구한다.")
void of(Square sourceSquare, Square targetSquare, Direction expectedDirection) {
// expected
assertThat(Direction.of(sourceSquare, targetSquare)).isEqualTo(expectedDirection);
}
그렇다면 어떻게 재정의 해야될까?
public class Piece {
private final Role role;
private final Side side;
private final Square square;
// ...
@Override
public String toString() {
return "Piece{" +
"role=" + role +
'}';
}
}
a2
의 위치에 있는 백
의 PAWN
과 a7
의 위치에 있는 흑
dml PAWN
은Piece{role=PAWN}
으로 출력될 것이다.public abstract class AbstractCollection<E> implements Collection<E> {
// ...
public String toString() {
Iterator<E> it = iterator();
if (!it.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (; ; ) {
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
if (!it.hasNext())
return sb.append(']').toString();
sb.append(',').append(' ');
}
}
// [0번째, 1번째, 2번쨰, ... ]
// ...
}
[요소1, 요소2, ...]
모든 요소를 반환한다.public abstract class AbstractMap<K,V> implements Map<K,V> {
// ...
public String toString() {
Iterator<Entry<K,V>> i = entrySet().iterator();
if (! i.hasNext())
return "{}";
StringBuilder sb = new StringBuilder();
sb.append('{');
for (;;) {
Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
sb.append(key == this ? "(this Map)" : key);
sb.append('=');
sb.append(value == this ? "(this Map)" : value);
if (! i.hasNext())
return sb.append('}').toString();
sb.append(',').append(' ');
}
}
// {키1=밸류1, 키2=밸류2, ...}
// ...
}
{키1:밸류1, 키2, 밸류2, ...}
를 반환한다.public class Piece {
private final Role role;
private final Color color;
// ...
@Override
public String toString() {
return "Piece{" +
"role=" + role +
", color=" + color +
'}';
}
}
public class Board {
private final Map<Square, MovablePiece> board;
private Side turn;
}
스퀘어=말
이 모두 반환된다. @Override
public String toString() {
return "Board{" +
"board의 Piece 개수=" + board.size() +
", turn=" + turn +
'}';
}
public class Square {
private final File file;
private final Rank rank;
@Override
public String toString() {
return "Square{" +
"file=" + file +
", rank=" + rank +
'}';
}
}
public class Square {
private final File file;
private final Rank rank;
/**
* Square의 문자열을 반환한다.
* 이 문자열은 "a2" 형태로 2글자로 구성된다.
* a는 소문자 알파벳으로 file을 나타낸다.
* 2는 10진수 숫자로 rank를 나타낸다.
*/
@Override
public String toString() {
return String.format("%s%s", file, rank);
}
}
public class Square {
private static final Map<File, Map<Rank, Square>> CACHE;
private final File file;
private final Rank rank;
public static Square of(String input) {
int fileValue = input.charAt(0) - 'a' + 1;
int rankValue = input.charAt(1) - '1' + 1;
File file = File.from(fileValue);
Rank rank = Rank.from(rankValue);
Map<Rank, Square> rankSquareMap = CACHE.get(file);
return rankSquareMap.get(rank);
}
@Override
public String toString() {
return String.format("%s%s", file, rank);
}
}
"a2"
인 값을 매개변수로 받아서 객체를 반환하는 정적 메서드 또한 만들 수 있다.public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {
private final String name;
// ...
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
// ...
public String toString() {
return name;
}
}
public class Piece {
private final Role role;
private final Color color;
private final Square square;
// ...
@Override
public String toString() {
return "Piece{" +
"role=" + role +
", color=" + color +
", square=" + square +
'}';
}
}
public class Square {
private final File file;
private final Rank rank;
private final Piece piece;
@Override
public String toString() {
return "Square{" +
"file=" + file +
", rank=" + rank +
", piece=" + piece +
'}';
}
}
Piece
↔ Square
을 서로를 toString에서 부르게 되면,