Item 29 : 이왕이면 제네릭 타입으로 만들라
2021. 7. 29. 01:04
제네릭을 새로 만드는 법이다.
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;
}
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // 다 쓴 참조 해제
return result;
}
public boolean isEmpty(){
return size =0;
}
private void ensureCapacity() {
if(elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
이 클래스를 제네릭으로 바꾸는 작업을 할 것이다.
이 Stack 클래스의 핵심은 스택에서 꺼낸 객체의 형을 어떻게 유지시키냐이다.
지금은 Object로 객체를 받고 있지만 제네릭으로 바꿀때의 첫단계는 클래스 선언에 타입 매개변수를 추가하는 일이다.
위 클래스에서는 스택이 담을 원소의 타입 하나만 추가하면되는데 보통 E를 사용한다.
그런 다음 코드에 쓰인 Object를 적절한 타입 매개변수로 바꾸면 된다.
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(Object e) {
ensureCapacity();
elements[size ++] = e;
}
public E pop() {
if (size == 0)
throw new EmptyStackException();
E result = elements[--size];
elements[size] = null; // 다 쓴 참조 해제
return result;
}
public boolean isEmpty(){
return size =0;
}
private void ensureCapacity() {
if(elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
이렇게 하면 generic array creation 오류가 발생한다
elements = new E[DEFAULT_INITIAL_CAPACITY];
이 부분에서 오류가 발생하는데 E와 같은 실체화 불가 타입으로는 배열을 만들 수 없기 때문이다.
해결책은 두가지 인데
- 제네릭 배열 생성을 금지하는 제약을 대놓고 우회하는 방법
//배열 elements는 push(E)로 넘어온 E 인스턴스만 남는다.
//따라서 타입 안정성을 보장하지만, 이배열의 런타입 타입은 E가 아닌 Object[]다
@SuppressWarnings("unchecked")
public Stack(){
elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
}
이렇게 만들고 @SuppressWarnings 어노테이션으로 경고를 숨기면 된다.
두번째 방법은
- elements 필드의 타입을 E[]에서 Object[]로 바꾸는 것이다.
public E pop() {
if (size == 0)
throw new EmptyStackException();
//push에서 E타입만 허용하므로 이 형변환은 안전하다.
@SuppressWarnings("unchecked") E result = (E) elements[--size];
elements[size] = null; // 다 쓴 참조 해제
return result;
}
첫번째 방법은 가독성이 좋다는 점이다 그러나 힙 오염 때문에 두번째 방식을 고수하기도 한다.
(현업에서는 첫번째 방법을 많이 사용한다.)
사용자가 하나하나 형변환 해줘야 하는 상황에서는 제네릭을 고려해봐야한다는 것이 이번 장의 포인트이다.
'책 > 이펙티브자바' 카테고리의 다른 글
Item 31 : 한정적 와일드카드를 사용해 API유연성을 높이라 (0) | 2021.07.31 |
---|---|
Item 30 : 이왕이면 제네릭 메소드로 만들라 (0) | 2021.07.29 |
Item 28 : 배열보다는 리스트를 사용하라 (0) | 2021.07.27 |
Item 27 : 비검사 경고를 제거하라 (0) | 2021.07.26 |
Item 26 : 로 타입을 사용하지 말라 (0) | 2021.07.25 |