개발하는 두더지

[Effective Java 규칙28] 한정적 와일드카드를 써서 API 유연성을 높여라 본문

Java,Android

[Effective Java 규칙28] 한정적 와일드카드를 써서 API 유연성을 높여라

덜지 2018. 10. 5. 17:22

[Effective Java 규칙28] 한정적 와일드카드를 써서 API 유연성을 높여라

Effective Java 2/E 책과 구글링을 통해 내용을 정리하고 개인적인 견해가 포함된 글입니다.



유연성을 최대화하려면, 객체를 만드는 생산자 Producer나 객체를 사용하는 소비자 Consumer 역할을 하는 메서드 인자의 자료형은 와일드 카드 자료형으로 하면 된다. 만약 메서드 인자가 생산자이면서 소비자라면 와일드카드 자료형은 필요없다.

PECS( Producer - extends ,  Consumer - super ) 을 사용하면 된다.


예를들어 Collection 인자가 E 생산자라면  

Collection<? extends E>


Collection인자가  E 소비자라면 

Collection<? super E>


<? extends E> 는 E의 하위 자료형의 Collection이다.  <E> 라고 한다면 E의 Collection이기때문에 인자로 넣을 수 있는 범위가 달라진다.

대표적인 예로 자바에는 Byte, Double, Float, Integer, Long, Short 자료형을 사용하는 Number 래퍼 클래스가 있다. 



class Stack<E> {
List<E> elements;
public Stack() {
elements = new ArrayList<>();
}

public void push(E e) {
elements.add(e);
}

public E pop() {
E e = elements.get(elements.size()-1);
elements.remove(e);
return e;
}

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

public void pushAll(Collection<? extends E> src) {
for(E e : src)
push(e);
}

public void popAll(Collection<? super E> dst) {
while(!isEmpty()) {
dst.add(pop());
}
}
}

이 클래스는 E 타입의 자료형을 저장할 수 있는 스택 자료구조 형태의 클래스이다. 



class Test {
public static void main(String[] args) {

Collection<Integer> integers = new ArrayList<>();
integers.add(10);
integers.add(20);
integers.add(30);
Stack<Number> numberStack = new Stack<>();
numberStack.pushAll(integers);

Collection<Object> objects = new ArrayList<>();
numberStack.popAll(objects);
for(Object o : objects) {
System.out.println(o);
}
}
}

스택이 Number 타입의 자료를 받아서 저장하고 꺼낼 수 있게 만들어 놓았다. 

만약 pushAll 메서드가 <E> 타입으로 선언되었다면 위의 코드는 에러가 발생한다. Integer가 Number의 하위 자료형이기 때문에 문제가 없을 것 같지만 형인자 자료형인 List는 불변이기 때문에 다른 타입을 저장할 수 없다. 

그래서 와일드카드 ? 를 사용하여 위의 코드를 문제없이 동작시킬 수 있는 것이다.




List<ScheduledFuture<?>> scheduledFutures = new ArrayList<>();
Sample.max(scheduledFutures);
Sample.maxEx(scheduledFutures);
class Sample{
public static <T extends Comparable<T>> T max(List<T> list) {
Iterator<T> i = list.iterator();
T result = i.next();
while(i.hasNext()) {
T t = i.next();
if(t.compareTo(result) > 0)
result = t;
}
return result;
}

public static <T extends Comparable<? super T>> T maxEx(List<? extends T> list) {
Iterator<? extends T> i = list.iterator();
T result = i.next();
while(i.hasNext()) {
T t = i.next();
if(t.compareTo(result) > 0)
result = t;
}
return result;
}
}


max는 컴파일 에러가 발생하지만 maxEx는 에러가 발생하지 않는다.








Comments