IT/Programming / / 2023. 4. 27. 09:27

<Effective Java> RULE 26 가능하면 제네릭 자료형으로 만들 것

반응형

 

// 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)

 

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유