public E pop() { if (size == 0) thrownewEmptyStackException(); @SuppressWarnings("unchecked") Eresult= (E) elements[--size]; elements[size] = null; return result; }
publicbooleanisEmpty() { return size == 0; }
privatevoidensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); } }
첫 번째 방법은 제네릭 배열 생성을 금지하는 제약을 대놓고 우회하는 방법이다. 첫 번째 방법의 장점은 가독성이다. 코드도 두 번째보다 짧다. 게다가 첫 번째 방법은 형 변환을 배열 생성시 단 한번만 해주면 된다. 하지만 이 방법은 E가 Object가 아닌 한 배열의 런타임 타입이 컴파일 타입과 달라 힙 오염을 일으킨다.
두 번째 방법은 elements 필드의 타입을 E[]에서 Object[]로 바꾸는 것이다. 힙 오염을 예방할 수는 있지만 배열에서 원소를 읽을 때 마다 형 변환을 수행한다.
제네릭 타입 안에서 리스트를 사용하는게 항상 가능하지도, 꼭 더 좋은 예도 아니다. 자바가 리스트를 기본 타입으로 제공하지 않으므로 ArrayList 같은 제네릭 타입도 결국 기본 타입인 배열을 사용해 구현해야 한다. 또한 HashMap 같은 제네릭 타입은 성능을 위해 배열을 사용한다.
/** * Shared empty array instance used for empty instances. */ privatestaticfinal Object[] EMPTY_ELEMENTDATA = {};
/** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. */ privatestaticfinal Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. */ transient Object[] elementData; // non-private to simplify nested class access
Stack 처럼 대다수의 제네릭 타입은 타입 매개변수에 아무런 제약을 두지 않는다. 단, 기본 타입은 제외하고 말이다. Stack<int> 이는 자바 제네릭의 근본적인 문제다.
타입 매개변수에 제약을 두는 제네릭 타입도 있다. class DelayQueue<E extends Delayed> implements BlockingQueue<E> 타입 매개변수 목록인 <E extends Delayed>는 java.util.concurrent.Delayed의 하위 타입만 받는다는 뜻이다. 이러한 타입 매개변수를 한정적 타입 매개변수라고 한다. 모든 타입은 자기 자신의 하위 타입이므로 DelayQueue로도 사용할 수 있음을 기억하자.