// Object를 사용한 Stack Class
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;
}
private void ensureCapacity(){
if(elements.length == size){
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}
· 위의 클래스는 객체를 꺼낼 때 형변환이 필요하다!
· 형변환은 프로그램 실행 중 실패할 가능성이 있다. → 제네릭으로 변경!
클래스를 제네릭화
· 선언부에 형인자를추가(E)
· 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;
}
}
//컴파일 되지 않는다!
//Cannot create a generic array of E
//E와같은 실체화 되지 않는 자료형으로는 배열을 생성할 수 없다.
해결방법 2가지
1. Object의 배열을 만들어 제네릭 배열자료형으로 형변환 하는법(Warning!-Unchecked Cast)
public Stack(){
elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
}
//Type safety: Unchecked cast from Object[] to E[]
public E pop(){
if(size == 0)
throw new EmptyStackException();
E result = (E)elements[--size];
elements[size] = null;
return result;
}
//Warining - unchecked Cast
elements는 private필드이며 클라이언트에 반환되지 않고 다른 어떤 메서드에게도 전달되지 않는다.
push메소드에 전달되는 원소만이 배열에 저장되며, 그 타입은 전부 E기 때문에 uncheck 형변환해도 문제가 없다.
//무점검 형변환이 안전하다는것을 입증하여 pop안의 해당부분에만 경고억제!
public E pop(){
if(size = 0)
throw new EmptyStackException();
@SuppressWarinings("unchecked") E result = (E)element[--size];
element[size] = null;
return result;
}
2. elements의 자료형을 E[]에서 Obejct[]로 바꾸는 것
private Object[] elements;
...
public E pop(){
if(size == 0)
throw new EmptyStackException();
E result = elements[--size];
elements[size] = null;
return result;
}
//Type mismatch: cannot convert from Object to E
public E pop(){
if(size == 0)
throw new EmptyStackException();
E result = (E) elements[--size];
elements[size] = null;
return result;
}
//Type safety: Unchecked cast from Object to E
어떤 방법이 더 나은가??
배열을 사용하는 코드가 여러 군데에 있다면 첫번째 코드는 E[]로 한번만 형변환 하면 되기 때문에 보편적으로 사용된다.
어떤 방법이 더 나은가??
배열을 사용하는 코드가 여러 군데에 있다면 첫번째 코드는 E[]로 한번만 형변환 하면 되기 때문에 보편적으로 사용된다.
// *형인자를 제한하는 제네릭 자료형
class DelayQueue<E extends Delayed> implements BlockingQueue<E>;
· 실 형인자 E는 반드시 java.util.concurrent.Delayed의 하위 자료형이어야 한다.
· DelayQueue에 저장하는 원소들은 명시적으로 Delayed 자료형으로 형변환하거나 ClassCastException이 발생할 위험을 무릅쓰지 않아도 Delayed에 선언된 메서드를 각각의 원소에 적용할 수 있다.
결론
· 제네릭 자료형은 클라이언트가 형변환을 해야만 사용할 수 있는 자료형보다 안전할 뿐 아니라 사용하기도 쉽다.
· 제네릭을 사용하여 새로운 자료형을 설계할 때에는 형변환 없이 사용할 수 있도록 하라
· 시간이 날때마다 기존 자료형을 제네릭 자료형으로 변환하라, 그러면 새로운 사용자에게 더 좋은 API를 제공할 수 있다.(규칙 23)
'IT > Programming' 카테고리의 다른 글
<Effective Java> RULE 24 무점검 경고(unchecked warining)을 제거하라 (0) | 2023.04.27 |
---|---|
<Effective Java> RULE 25 배열대신 리스트를 써라 (0) | 2023.04.27 |
<Effective Java> RULE 27 가능하면 제네릭 메서드로 만들 것 (0) | 2023.04.27 |
<Effective Java> RULE 28 한정적 와일드카드를 써서 API 유연성을 높여라 (0) | 2023.04.27 |
<Effective Java> RULE 29 형안전 다형성 컨테이너를 쓰면 어떨지 따져보라 (0) | 2023.04.27 |