728x90
핵심 정리
클라이언트에서 직접 형변환해야 하는 타입보다 제네릭 타입이 더 안전하고 쓰기 편하다. 그러니 새로운 타입을 설계할 때는 형변환 없이도 사용할 수 있도록 하라. 그렇게 하려면 제네릭 타입으로 만들어야 할 경우가 많다. 기존 타입 중 제네릭이었어야 하는 게 있다면 제네릭 타입으로 변경하자. 기존 클라이언트에는 아무 영향을 주지 않으면서, 새로운 사용자를 훨씬 편하게 해주는 길이다.
Object 기반 스택
//Object 기반 스택 - 제네릭이 절실한 강력 후보
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
private void ensureCapacity() {
if (elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
}
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // 다 쓴 참조 해제
return result;
}
}
이 클래스는 원래 제네릭 타입이어야 마땅하다. 그러나 제네릭으로 만들어보자. 이 클래스를 제네릭으로 바꾼다고 해도 현재 버전을 사용하는 클라이언트에는 아무런 해가 없다. 오히려 지금 상태에서의 클라이언트는 스택에서 꺼낸 객체를 형변환해야 하는데, 이때 런타임 오류가 날 위험이 있다.
Object -> 제네릭 스택<E>
//제네릭 스택으로 가는 첫 단계 - 컴파일되지 않는다.
public class Stack<E> {
private E[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
elements = new E[DEFAULT_INITIAL_CAPACITY];
}
public void push(E e) {
ensureCapacity();
elements[size++] = e;
}
public E pop() {
if (size == 0)
throw new EmptyStackException();
E result = elements[--size];
elements[size] = null; // 다 쓴 참조 해제
return result;
}
}
오류 발생
Stack.java:8 generic array creation
elements = new E[DEFAULT_INITIAL_CAPACITY];
^
item 28에서 설명한 것 처럼, E와 같은 실체화 불가 타입으로는 배열을 만들 수 없다. 배열을 사용하는 코드를 제네릭으로 만들려 할 때는 이 문제가 항상 발목을 잡을 것이다.
해결책
1. Object 배열을 생성한 다음 제네릭 배열로 형변환을 한다.
//컴파일러는 오류 대신 경고를 내보낼 것이다.
//이렇게도 할 수는 있지만 (일반적으로) 타입 안전하지 않다.
public Stack() {
elements = (E[])new Object[DEFAULT_INITIAL_CAPACITY];
}
//비검사 형변환이 안전함을 증명했다면 범위를 최소로 좁혀
//@SuppressWarnings 애너테이션으로 해당 경고를 숨긴다.
//애너테이션을 달면 Stack은 깔끔히 컴파일되고, 명시적으로 형변환하지 않아도
//ClassCastException 걱정 없이 사용할 수 있다.
@SuppressWarnings("unchecked")
public Stack() {
elements = (E[])new Object[DEFAULT_INITIAL_CAPACITY];
}
2. elements 필드의 타입을 E[ ]에서 Object[ ]로 바꾼다.
//배열이 반환한 원소를 E로 형변환하면 오류 대신 경고가 뜬다.
//오류 - Type mismatch: cannot convert from Object to E
E result = elements[--size];
//배열이 반환한 원소를 E로 형변환하면 오류 대신 경고가 뜬다.
//경고 - Type safety : Unchecked sast from Object to E
//E는 실체화 불가 타입이므로 컴파일러는 런타임에 이뤄지는 형변환이 안전한지 증명할 방법이 없다.
E result = (E)elements[--size];
//item27의 조언을 따라 비검사 형변환을 수행하는 할당문에서만 숨기자.
@SuppressWarnings("unchecked")
E result = (E)elements[--size];
'일상 > 레벨업 독서' 카테고리의 다른 글
[이펙티브 자바] Item 37 ordinal 인덱싱 대신 EnumMap을 사용하라. (0) | 2022.04.12 |
---|---|
[이펙티브 자바] Item 36 비트 필드 대신 EnumSet을 사용하라. (0) | 2022.04.11 |
[이펙티브 자바] Item 28 배열보다는 리스트를 사용하라. (0) | 2022.04.04 |
[이펙티브 자바] Item27 비검사 경고를 제거하라 (0) | 2022.04.04 |
[이펙티브 자바] Item16 public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라. (0) | 2022.03.27 |