JDK가 제공하는 제네릭 타입과 메서드를 사용하는 일은 일반적으로 쉬운 편이다. 하지만 제네릭 타입을 새로 만드는 일은 조금 더 어렵다.
public class StackBasedObject {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public StackBasedObject() {
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);
}
}
}
위 클래스는 제네릭 타입이어야 마땅하다.
public class StackGeneric<E> {
private E[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public StackGeneric() {
elements = new Object[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;
}
public boolean isEmpty() {
return size == 0;
}
private void ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}
위 예제에서, E와 같은 실체화 불가 타입으로는 배열을 만들 수 없다. 이에 대한 해결책은 두 가지다.
public class StackGeneric<E> {
private E[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
// 배열 elements는 push(E)로 넘어온 E 인스턴스만 담는다.
// 따라서 타입 안전성을 보장하지만, 이 배열의 런타임 타입은 E[]가 아닌 Object[]다.
@SuppressWarnings("unchecked")
public StackGeneric() {
elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
}
// ...
}
public class StackGeneric<E> {
private Object[] elements; // Obejct[] 타입이다!
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public StackGeneric() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public E pop() {
if (size == 0) {
throw new EmptyStackException();
}
@SuppressWarnings("unchecked") // push에서 E 타입만 허용하므로 이 형변환은 안전하다.
E result = (E) 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>
, Stack<int[]>
, Stack<List<String>>
등등.. 어떤 참조 타입으로도 Stack을 만들 수 있다.class DelayQueue<E extends Delayed> implements BlockingQueue<E> {
// ...
}
<E extend Delayed>
는 java.util.concurrent.Delayed
의 하위 타입만 받는다는 뜻이다.DelayQueue<Delayed>
로도 사용할 수 있다.