이펙티브 자바 아이템 31. 한정적 와일드카드를 사용해 API의 유연성을 높혀라. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 package com.github.sejoung.codetest.generics.bounded;import java.util.ArrayList;import java.util.Arrays;import java.util.Collection;public class Stack <E> { private E[] elements; private int size = 0 ; private static final int DEFAULT_INITIAL_CAPACITY = 16 ; @SuppressWarnings("unchecked") public Stack () { elements = (E[]) new Object [DEFAULT_INITIAL_CAPACITY]; } public void push (E e) { ensureCapacity(); elements[size++] = e; } public E pop () { if (size==0 ) throw new RuntimeException (); E 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 ); } public void pushAll (Iterable<E> src) { for (E e : src) push(e); } public void popAll (Collection<E> dst) { while (!isEmpty()) dst.add(pop()); } public static void main (String[] args) { Stack<Number> numberStack = new Stack <>(); Iterable<Integer> integers = Arrays.asList(3 , 1 , 4 , 1 , 5 , 9 ); numberStack.pushAll(integers); Collection<Object> objects = new ArrayList <>(); numberStack.popAll(objects); System.out.println(objects); } }
컴파일에러
1 2 Error:(76, 29) java: incompatible types: java.lang.Iterable<java.lang.Integer> cannot be converted to java.lang.Iterable<java.lang.Number> Error:(79, 28) java: incompatible types: java.util.Collection<java.lang.Object> cannot be converted to java.util.Collection<java.lang.Number>
위와 같은 에러가 난다. 보기에는 당연히 되어야 할것 같은데 에러가 난다. 제네릭은 불공변이기 때문이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 package com.github.sejoung.codetest.generics.bounded;import java.util.ArrayList;import java.util.Arrays;import java.util.Collection;public class Stack <E> { private E[] elements; private int size = 0 ; private static final int DEFAULT_INITIAL_CAPACITY = 16 ; @SuppressWarnings("unchecked") public Stack () { elements = (E[]) new Object [DEFAULT_INITIAL_CAPACITY]; } public void push (E e) { ensureCapacity(); elements[size++] = e; } public E pop () { if (size==0 ) throw new RuntimeException (); E 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 ); } public void pushAll (Iterable<? extends E> src) { for (E e : src) push(e); } public void popAll (Collection<? super E> dst) { while (!isEmpty()) dst.add(pop()); } public static void main (String[] args) { Stack<Number> numberStack = new Stack <>(); Iterable<Integer> integers = Arrays.asList(3 , 1 , 4 , 1 , 5 , 9 ); numberStack.pushAll(integers); Collection<Object> objects = new ArrayList <>(); numberStack.popAll(objects); System.out.println(objects); } }
실행결과
1 2 3 4 [9, 5, 1, 4, 1, 3] Process finished with exit code 0
위에 처럼 자바에서는 이런상황을 대처하기 위해 와일드 카트 타입을 지원한다.
유연성을 극대화 하기위해서는 생산자나 소비자에 입력매개변수의 와일드타입을 사용하라.
1 2 3 4 PECS(Produce - Extends, Consumer - Super) <? extends T>, <? super T>
위에 내용을 기억하라 그래서 pushAll 과 popAll을 비교해보면 된다.
생성자의 적용 내용
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package com.github.sejoung.codetest.generics.bounded;import java.util.ArrayList;import java.util.Collection;import java.util.List;import java.util.Random;public class Chooser <T> { private final List<T> choiceList; private final Random rnd = new Random (); public Chooser (Collection choices) { choiceList = new ArrayList <>(choices); } public T choose () { return choiceList.get(rnd.nextInt(choiceList.size())); } public static void main (String[] args) { List<Integer> intList = List.of(1 , 2 , 3 , 4 , 5 , 6 ); Chooser<Number> chooser = new Chooser <>(intList); for (int i = 0 ; i < 10 ; i++) { Number choice = chooser.choose(); System.out.println(choice); } } }
컴파일메시지
1 2 3 4 5 6 7 8 9 10 Warning:(15, 22) java: unchecked method invocation: constructor <init> in class java.util.ArrayList is applied to given types required: java.util.Collection<? extends E> found: java.util.Collection Warning:(15, 38) java: unchecked conversion required: java.util.Collection<? extends E> found: java.util.Collection Warning:(15, 22) java: unchecked conversion required: java.util.List<T> found: java.util.ArrayList
위에 메시지를 없에기 위해
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.github.sejoung.codetest.generics.bounded;import java.util.ArrayList;import java.util.Collection;import java.util.List;import java.util.Random;public class Chooser <T> { private final List<T> choiceList; private final Random rnd = new Random (); public Chooser (Collection<? extends T> choices) { choiceList = new ArrayList <>(choices); } public T choose () { return choiceList.get(rnd.nextInt(choiceList.size())); } public static void main (String[] args) { List<Integer> intList = List.of(1 , 2 , 3 , 4 , 5 , 6 ); Chooser<Number> chooser = new Chooser <>(intList); for (int i = 0 ; i < 10 ; i++) { Number choice = chooser.choose(); System.out.println(choice); } } }
실행결과
1 2 3 4 5 6 7 8 9 10 11 12 6 1 3 1 3 5 5 5 6 4 Process finished with exit code 0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.github.sejoung.codetest.generics.bounded;import java.util.Arrays;import java.util.List;public class Swap { public static void swap (List<?> list, int i, int j) { list.set(i, list.set(j, list.get(i))); } public static void main (String[] args) { List<String> argList = Arrays.asList("a" ,"b" ,"c" ); swap(argList, 0 , argList.size() - 1 ); System.out.println(argList); } }
컴파일메시지
1 2 Error:(10, 41) java: incompatible types: java.lang.Object cannot be converted to capture#1 of ?
위에 처럼 컴파일 되지 않는데 문제는< ? > 카드 타입 때문이다. 이것은 null 빼고는 아무것도 못넣는데 있다 하지만 해결방법이 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.github.sejoung.codetest.generics.bounded;import java.util.Arrays;import java.util.List;public class Swap { public static void swap (List<?> list, int i, int j) { swapHelper(list, i, j); } private static <E> void swapHelper (List<E> list, int i, int j) { list.set(i, list.set(j, list.get(i))); } public static void main (String[] args) { List<String> argList = Arrays.asList("a" ,"b" ,"c" ); swap(argList, 0 , argList.size() - 1 ); System.out.println(argList); } }
실행결과
1 2 3 [c, b, a] Process finished with exit code 0
참조