peaches-book-study / effective-java

이펙티브 자바 3/E
0 stars 2 forks source link

Item 29. 이왕이면 제네릭 타입으로 만들라 #29

Open Lainlnya opened 4 months ago

Lainlnya commented 4 months ago

Chapter : 5. 제네릭

Item : 29. 이왕이면 제네릭 타입으로 만들라

Assignee : Lainlnya


🍑 서론

제네릭 타입을 새로 만들어라

🍑 본론

제네릭 만들기

✅ Object 기반 스택 => 제네릭을 추천하는 이유

    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;
        }

        public boolean isEmpty() {
            return size == 0;
        }

        private void ensureCapacity() {
            if (elements.length == size) {
                elements = Arrays.copyOf(elements, 2 * size + 1);
            }
        }
    }
  1. 클래스 선언에 타입 매개변수를 추가
    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;
        }
    }

❌ E 같은 실체화 불가 타입으로는 배열을 만들 수 었다.

⭕ 해결책

  1. 제네릭 배열 생성을 금지하는 제약을 대놓고 우회하는 방법(선호)

    하지만 아래의 경우 타입 안정성 경고가 발생한다. => 사용자가 확인

    // Stack.java:8: warning: [unchecked] unchecked cast
    // found: Object[], required: E[]
    
    elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];

    ✅ elements는 private 필드에 저장되고, 클라이언트로 반환되거나 다른 메서드에 전달되는 일이 없다. \ ✅ push 메서드를 통해 배열에 저장되는 원소의 타입은 항상 E다

    => 안전함을 판단한 뒤 @SuppressWarning으로 경고를 숨긴다.

    // 타입 안정성은 보장하지만, 배열의 런타임 타입은 E[]가 아닌 Object[]다.
    @SuppressWarning("unchecked")
    public Stack() {
        elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
    }
  2. elements 필드의 타입을 E[]에서 Object[]로 바꾸는 것

    // Stack.java:19: warning: [unchecked] unchecked cast
    // found: Object, required: E
    
    E result = (E) elements[--size];
    

    E는 실체화 불가 타입이기 때문에 런타임에 이뤄지는 형변환이 안전한지 증명할 수 없다. => 사용자가 확인한 후 경고를 숨길 수 있다.

    public E pop() {
    if (size == 0) throw new EmptyStackException();
    
    @SuppressWarnings("unchecked")
    E result = (E) elements[--size];
    elements[size] = null; // 다 쓴 참조 객체
    return result;
    }

두 방식의 장단점

배열과 리스트

위의 Stack 예시는 "배열보다는 리스트를 우선하라"는 원칙과 모순되어보인다.

자바가 리스트를 기본 타입으로 제공하지 않기 때문에 ArrayList 같은 제네릭 타입도 기본 타입인 배열을 사용해 구현해야하며, HashMap 같은 제네릭 테입은 성능을 높일 목적으로 배열을 사용하기도 한다.

제네릭 타입

대다수의 제네릭 타입은 타입 매개변수에 아무런 제약을 두지 않지만, 기본 타입은 사용할 수 없다. (int, double ,,,)

✅ 타입 매개변수에 제약을 두는 제네릭 타입

// java.util.concurrent.DelayQueue
class DelayQueue<E extends Delayed> implements BlockingQueue<E> 

E extends Delayed의 의미란 java.util.concurrent.Delayed의 하위 타입만 받는 다는 뜻 => DelayQueue의 원소에서 형변환 없이 곧바로 Delayed 클래스의 메서드를 호출할 수 있으며, 이러한 타입 매개변수 E를 한정적 타입 매개변수(bounded type parameter)라고 한다. => 모든 타입은 자기 자신의 하위 타입이기 때문에 <E extends Delayed> 대신에 DelayQueue<Delayed>로도 사용할 수 있다.

🍑 결론

새로운 타입을 설계할 때는 형변환 없이도 사용할 수 있도록 하는 것이 좋은데, 그럴 때는 제네릭 타입으로 만들어야 할 경우가 많다. 기존 타입 중 제네릭이었어야 하는게 있다면 제네릭 타입으로 변경하자.


Referenced by

hyunsoo10 commented 3 months ago

힙 오염이 뭐에요?

Lainlnya commented 3 months ago

힙 오염이 뭐에요?

https://kimalam.blogspot.com/2019/09/java-heap-pollution.html 참고해주세요 ^_^

jseok0917 commented 3 months ago

https://catsbi.oopy.io/04f05c14-5a1b-464b-b889-27f38c31f9b8

힙 오염이 뭐에요?

Lainlnya commented 3 months ago

힙 오염이 뭐에요?

https://kimalam.blogspot.com/2019/09/java-heap-pollution.html 참고해주세요 ^_^

힙오염의 의미:

특정 타입의 참조로 예상되는 컬렉션이나 배열 등에 잘못된 타입의 객체가 저장되어 프로그램의 다른 부분에서 타입 캐스팅 오류가 발생하는 상황