-
[Effective Java] 아이템29: 이왕이면 제네릭 타입으로 만들라Language/Java 2022. 8. 6. 09:32
아이템7에서 다룬 스택 코드는 원래
제네릭 타입
이어야 마땅하다.다음과 같이 타입 매개변수 E를 추가하고, Object를 적절한 타입 매개변수로 바꾸어준다.
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; } // isEmpty와 ensureCapacity 메서드는 그대로다. ... }
위의 코드는
new E[DEFAULT_INITIAL_CAPACITY]
부분에서 오류가 발생한다.이를 해결하기 위한 방법은 두 가지가 있다.
1️⃣ Object 배열을 생성한 다음 제네릭 배열로 형변환하기
// 배열 elements는 push(E)로 넘어온 E 인스턴스만 담는다. // 따라서 타입 안전성을 보장하지만, // 이 배열의 런타임 타입은 E[]가 아닌 Object[]다! @SuppressWarnings("unchecked") public Stack() { elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY]; }
이렇게 수정하면, 컴파일러에서
unchecked cast
경고를 내보낸다.그러나 이 경우에 elements 배열은 private 필드에 저장되고, push 메서드로 전달되는 원소의 타입은 항상 E이므로 비검사 형변환은 안전하다. 비검사 형변환이 안전하기 때문에
@SuppressWarnings
애너테이션을 달아 경고를 숨겼다.이 방법은 가독성이 좋고, 뒤의 방법보다 코드도 더 짧다.
2️⃣ 필드 타입을 Object[]로 바꾸기
public class Stack<E> { private Object[] elements; ... // 비검사 경고를 적절히 숨긴다. public E pop() { if (size == 0) throw new EmptyStackException(); // push에서 E 타입만 허용하므로 이 형변환은 안전하다. @SuppressWarnings("unchecked") E result = (E) elements[--size]; elements[size] = null; // 다 쓴 참조 해제 return result; } }
pop() 메서드에서 반환한 원소를 E로 형변환하면 컴파일러가
unchecked cast
경고를 내보낸다.이번에도 형변환이 안전하다고 증명하고, 위와 같이 경고를 숨길 수 있다.
이 방법은 배열에서 원소를 읽을 때마다 형변환을 해주어야 한다.
그러나 첫 번째 방식은 배열의 런타임 타입과 컴파일타임 타입이 달라 힙 오염(heap pollution)을 일으킨다.핵심 정리
- 클라이언트에서 직접 형변환해야 하는 타입보다 제네릭 타입이 더 안전하고 쓰기 편하다.
그러니 새로운 타입을 설계할 때는 형변환 없이도 사용할 수 있도록 하라. - 그렇게 하려면 제네릭 타입으로 만들어야 할 경우가 많다.
기존 타입 중 제네릭이었어야 하는 게 있다면 제네릭 타입으로 변경하자. - 기존 클라이언트에는 아무 영향을 주지 않으면서, 새로운 사용자를 후러씬 편하게 해주는 길이다. (아이템 26)
728x90'Language > Java' 카테고리의 다른 글
[Effective Java] 아이템31: 한정적 와일드카드를 사용해 API 유연성을 높이라 (0) 2022.08.06 [Effective Java] 아이템30: 이왕이면 제네릭 메서드로 만들라 (0) 2022.08.06 [Effective Java] 아이템28: 배열보다는 리스트를 사용하라 (0) 2022.07.30 [Effective Java] 아이템27: 비검사 경고를 제거하라 (0) 2022.07.29 [Effective Java] 아이템26: 로 타입(raw type)은 사용하지 말라 (0) 2022.07.29 - 클라이언트에서 직접 형변환해야 하는 타입보다 제네릭 타입이 더 안전하고 쓰기 편하다.