그러니 새로운 타입을 설계하거나 기존 타입 중 제네릭이었어야 하는 것이 있다면,
형변환 없이도 사용 가능하도록 제네릭으로 만들자!
사용하기
Stack 클래스(#7)
class mystack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public mystack() {
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);
}
}
-> 이걸 제네릭 타입으로 바꿔보자!
타입 매개변수 추가
일반적으로 타입 이름은 E 사용(#68), 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]; // <--- 실체화 불가타입인 E로는 배열을 만들 수 없어, 오류 발생!
}
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 resuilt;
}
// 이하 생략
}
실체화 불가 타입으로 인해 오류 발생(#28)
배열을 사용하는 코드를 제네릭으로 만들고 싶다면 항상 발생하는 문제
해결책1 - 대놓고 우회하기
Object 배열을 생성한 다음, 제네릭 배열로 형변환
public Stack() {
elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
}
컴파일러는 이 타입이 과연 안전한지 증명할 수 없음 -> 개발자 스스로 확인해야 한다!
(현재 예제에서는 elements가 private이라 외부노출 x, push로 스택에 들어오는 타입은 무조건 E이기에 안전)
안전하다면? -> 경고 숨겨서 사용하기(#27)
@SuppressWarnings("unchecked")
public Stack() {
elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
}
public E pop() {
if (size == 0)
throw new EmptyStackException();
// push에서 E 타입만 허용하므로 이 형변환은 안전하다
@SuppressWarnings("unchecked")
E result = (E) elements[--size];
elements[size] = null; // 다쓴 참조 해제
return resuilt;
}
이 또한 컴파일러는 이 타입이 과연 안전한지 증명할 수 없어, 개발자가 직접 증명하고 숨겨야 한다!
장점
힙 오염 x
단점
원소를 읽을 때마다 처리해줘야 함
이외 제네릭 사용법 등
위의 예시는 배열보다는 리스트를 우선하라(#28) 것과 모순되는 듯함
사실 리스트를 항상 사용가능하지도 않고, 꼭 리스트가 더 좋지도 않다고 한다(...)
리스트를 기본 타입으로 제공하지 않으므로, ArrayList 등도 결국 배열을 사용해야하고, hashMap 등은 성능을 목적으로 배열을 사용하기도 한다.
제네릭의 근본 문제인 박싱된 기본 타입(#61)만 사용할 수 있음
한정적 타입 매개변수(bounded type parameter) 사용 가능
class DelayQueue<E extends Delayed> implements BlockingQueue<E>
개요
사용하기
Stack 클래스(#7)
-> 이걸 제네릭 타입으로 바꿔보자!
타입 매개변수 추가
제네릭 스택으로 가는 첫 단계! - 하지만 컴파일되지 않는다.
해결책1 - 대놓고 우회하기
Object 배열을 생성한 다음, 제네릭 배열로 형변환
안전하다면? -> 경고 숨겨서 사용하기(#27)
해결책 2 -필드 타입 변경
필드타입을 Object[]로 변경하고, 배열이 반환한 원소를 E로 형변환
이 또한 컴파일러는 이 타입이 과연 안전한지 증명할 수 없어, 개발자가 직접 증명하고 숨겨야 한다!
장점
단점
이외 제네릭 사용법 등