요약
클라이언트가 사용할 때 매번 형변환을 해야하고 그 과정에서 Runtime 에러가 나는 것을 방지하기 위해 이왕이면 제네릭 타입으로 만들자.
Object 배열로 이루어진 스택
public class ArrayStack {
private Object[] elements;
private int size;
public ArrayStack() {
this.elements = new Object[10];
}
public void push(Object obj) {
elements[size++] = obj;
}
public Object pop() {
if (size == 0) {
throw new IllegalArgumentException("스택에 아무것도 없습니다");
}
Object result = elements[--size];
elements[size] = null;
return result;
}
}
위와 같이 Object 배열을 가진 스택을 사용해보자
@DisplayName("Object리스트타입의 스택은 매번 형변환이 필요하다")
@Test
void basExample() {
Bad bad = new Bad("BadExample1");
ArrayStack arrayStack = new ArrayStack();
arrayStack.push(bad);
// Bad findBad = arrayStack.pop(); CompileError!
// String maybeBad = (String) arrayStack.pop(); RunTimeError!
}
class Bad {
private String value;
public Bad(String value) {
this.value = value;
}
}
클라이언트는 매번 형변환을 해야하고, 형변환이 반드시 성공할 것이라는 보장이 없기 때문에 Runtime Exception 이 나올 수 있다.
이제 이 스택클래스를 제네릭으로 바꿔보며 제네릭으로 바꿨을 때의 장점을 알아볼 것이다
먼저, 모든 Object를 제네릭 E로 변경하면 다음과 같이 컴파일 에러가 발생한다.
E는 소거되기 때문인데, 컴파일 에러를 해결할 수 있는 방법은 2가지가 있다.
- 대놓고 우회하는 방법: 생성자의 컴파일 에러를 다음으로 해결한다.
public GenericStack() { this.elements = (E[])new Object[10]; }
이 예제의 경우 E타입만 Push할 수 있기 때문에 자체적으로 형변환이 늘 안전하다는 것을 확신할 수 있다.
따라서 이렇게 우회하고 @SuppressWarnings 으로 비검사 오류를 숨길 수 있다.
장점: 필드에 E 배열 타입을 가지고 있음으로써 이 Stack은 E 타입의 인스턴스만 받을 수 있음을 확신할 수 있도록 한다.
단점: 힙 오염. 런타임타입: E, 컴파일타임 타입: Object
cf) 힙오염(https://en.wikipedia.org/wiki/Heap_pollution)
아래와 같이 컴파일타임타입, 런타임타입이 달라 Unchecked Warning 과 ClassCastException 이 발생할 가능성이 있는 상황.
스택 예의 경우 그럴 가능성은 없음
관련 자바봄 이슈) https://github.com/Java-Bom/ReadingRecord/issues/73
public class HeapPollutionDemo
{
public static void main(String[] args)
{
Set s = new TreeSet<Integer>();
Set<String> ss = s; // unchecked warning
s.add(new Integer(42)); // another unchecked warning
Iterator<String> iter = ss.iterator();
while (iter.hasNext())
{
String str = iter.next(); // ClassCastException thrown
System.out.println(str);
}
}
}
- 필드를 Object[] 으로 바꾸는 법: pop 메서드에 형변환과 @SuppressWarnings 를 추가한다.
@SuppressWarnings("unchecked") E result = (E) elements[--size];
장점: 힙오염 없음
단점: 원소를 pop 할 때마다 형변환
+ DelayQueue 관련 자바봄 이슈: https://github.com/Java-Bom/ReadingRecord/issues/77
'Reading Record > 이펙티브자바' 카테고리의 다른 글
[아이템 31] 한정적 와일드카드를 사용해 API 유연성을 높여라 (0) | 2020.03.29 |
---|---|
[아이템 30] 이왕이면 제네릭메서드로 만들라 (0) | 2020.03.29 |
아이템 [6] - 불필요한 객체생성을 피하라 (0) | 2020.03.04 |
아이템 [20] - 추상 클래스 보다는 인터페이스를 우선하라 (1) | 2020.02.29 |
아이템 [19] - 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라 (0) | 2020.02.27 |