
package me.whiteship.chapter05.item31;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EmptyStackException;
// 와일드카드 타입을 이용해 대량 작업을 수행하는 메서드를 포함한 제네릭 스택 (181-183쪽)
public class Stack<E> {
private E[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
// 코드 29-3 배열을 사용한 코드를 제네릭으로 만드는 방법 1 (172쪽)
// 배열 elements는 push(E)로 넘어온 E 인스턴스만 담는다.
// 따라서 타입 안전성을 보장하지만,
// 이 배열의 런타임 타입은 E[]가 아닌 Object[]다!
@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 EmptyStackException();
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);
}
// 코드 31-1 와일드카드 타입을 사용하지 않은 pushAll 메서드 - 결함이 있다! (181쪽)
// public void pushAll(Iterable<E> src) {
// for (E e : src)
// push(e);
// }
// 코드 31-2 E 생산자(producer) 매개변수에 와일드카드 타입 적용 (182쪽)
public void pushAll(Iterable<E> src) {
for (E e : src)
push(e);
}
// 코드 31-3 와일드카드 타입을 사용하지 않은 popAll 메서드 - 결함이 있다! (183쪽)
// public void popAll(Collection<E> dst) {
// while (!isEmpty())
// dst.add(pop());
// }
// 코드 31-4 E 소비자(consumer) 매개변수에 와일드카드 타입 적용 (183쪽)
public void popAll(Collection<? super E> dst) {
while (!isEmpty())
dst.add(pop());
}
// 제네릭 Stack을 사용하는 맛보기 프로그램
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);
// Iterable<Double> doubles = Arrays.asList(3.1, 1.0, 4.0, 1.0, 5.0, 9.0);
// numberStack.pushAll(doubles);
//
// Collection<Object> objects = new ArrayList<>();
// numberStack.popAll(objects);
//
// System.out.println(objects);
}
}
package me.whiteship.chapter05.item31;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EmptyStackException;
// 와일드카드 타입을 이용해 대량 작업을 수행하는 메서드를 포함한 제네릭 스택 (181-183쪽)
public class Stack<E> {
private E[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
// 코드 29-3 배열을 사용한 코드를 제네릭으로 만드는 방법 1 (172쪽)
// 배열 elements는 push(E)로 넘어온 E 인스턴스만 담는다.
// 따라서 타입 안전성을 보장하지만,
// 이 배열의 런타임 타입은 E[]가 아닌 Object[]다!
@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 EmptyStackException();
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);
}
// 코드 31-1 와일드카드 타입을 사용하지 않은 pushAll 메서드 - 결함이 있다! (181쪽)
// public void pushAll(Iterable<E> src) {
// for (E e : src)
// push(e);
// }
// 코드 31-2 E 생산자(producer) 매개변수에 와일드카드 타입 적용 (182쪽)
public void pushAll(Iterable<? extends E> src) {
for (E e : src)
push(e);
}
// 코드 31-3 와일드카드 타입을 사용하지 않은 popAll 메서드 - 결함이 있다! (183쪽)
public void popAll(Collection<E> dst) {
while (!isEmpty())
dst.add(pop());
}
// 코드 31-4 E 소비자(consumer) 매개변수에 와일드카드 타입 적용 (183쪽)
//public void popAll(Collection<? super E> dst) {
// while (!isEmpty())
// dst.add(pop());
//}
// 제네릭 Stack을 사용하는 맛보기 프로그램
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);
Iterable<Double> doubles = Arrays.asList(3.1, 1.0, 4.0, 1.0, 5.0, 9.0);
numberStack.pushAll(doubles);
Collection<Object> objects = new ArrayList<>();
numberStack.popAll(objects);
System.out.println(objects);
}
}
Number의 하위타입을 받아서 할 수 있기 때문에 들어갈 수 있다.
Double도 들어갈 수 있다.

package me.whiteship.chapter05.item31;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EmptyStackException;
// 와일드카드 타입을 이용해 대량 작업을 수행하는 메서드를 포함한 제네릭 스택 (181-183쪽)
public class Stack<E> {
private E[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
// 코드 29-3 배열을 사용한 코드를 제네릭으로 만드는 방법 1 (172쪽)
// 배열 elements는 push(E)로 넘어온 E 인스턴스만 담는다.
// 따라서 타입 안전성을 보장하지만,
// 이 배열의 런타임 타입은 E[]가 아닌 Object[]다!
@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 EmptyStackException();
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);
}
// 코드 31-1 와일드카드 타입을 사용하지 않은 pushAll 메서드 - 결함이 있다! (181쪽)
// public void pushAll(Iterable<E> src) {
// for (E e : src)
// push(e);
// }
// 코드 31-2 E 생산자(producer) 매개변수에 와일드카드 타입 적용 (182쪽)
public void pushAll(Iterable<? extends E> src) {
for (E e : src)
push(e);
}
// 코드 31-3 와일드카드 타입을 사용하지 않은 popAll 메서드 - 결함이 있다! (183쪽)
// public void popAll(Collection<E> dst) {
// while (!isEmpty())
// dst.add(pop());
// }
// 코드 31-4 E 소비자(consumer) 매개변수에 와일드카드 타입 적용 (183쪽)
public void popAll(Collection<? super E> dst) {
while (!isEmpty())
dst.add(pop());
}
// 제네릭 Stack을 사용하는 맛보기 프로그램
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);
Iterable<Double> doubles = Arrays.asList(3.1, 1.0, 4.0, 1.0, 5.0, 9.0);
numberStack.pushAll(doubles);
Collection<Object> objects = new ArrayList<>();
numberStack.popAll(objects);
//
// System.out.println(objects);
}
}
extends E : 타입 아래에 있는 것들, 상속받은 클래스들을 의미한다. 뭔가를 저장하는 쪽이면 extends
super E : E의 슈퍼 타입, 부모 클래스, 클래스가 구현하고 있는 인터페이스를 허용하는 것이다. 뭔가를 빼오는 쪽이면 super를 사용한다.
오브젝트는 Number의 Super인 것이다.


package me.whiteship.chapter05.item31;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
// T 생산자 매개변수에 와일드카드 타입 적용 (184쪽)
public class Chooser<T> {
private final List<T> choiceList;
private final Random rnd = new Random();
// 코드 31-5 T 생산자 매개변수에 와일드카드 타입 적용 (184쪽)
public Chooser(Collection<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);
}
}
}
package me.whiteship.chapter05.item31;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
// T 생산자 매개변수에 와일드카드 타입 적용 (184쪽)
public class Chooser<T> {
private final List<T> choiceList;
private final Random rnd = new Random();
// 코드 31-5 T 생산자 매개변수에 와일드카드 타입 적용 (184쪽)
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);
}
}
}
package me.whiteship.chapter05.item31;
import java.util.HashSet;
import java.util.Set;
// 코드 30-2의 제네릭 union 메서드에 와일드카드 타입을 적용해 유연성을 높였다. (185-186쪽)
public class Union {
public static <E> Set<E> union(Set<? extends E> s1,
Set<? extends E> s2) {
Set<E> result = new HashSet<>(s1);
result.addAll(s2);
return result;
}
// 향상된 유연성을 확인해주는 맛보기 프로그램 (185쪽)
public static void main(String[] args) {
Set<Integer> integers = new HashSet<>();
integers.add(1);
integers.add(3);
integers.add(5);
Set<Double> doubles = new HashSet<>();
doubles.add(2.0);
doubles.add(4.0);
doubles.add(6.0);
Set<Number> numbers = union(integers, doubles);
// // 코드 31-6 자바 7까지는 명시적 타입 인수를 사용해야 한다. (186쪽)
// Set<Number> numbers = Union.<Number>union(integers, doubles);
System.out.println(numbers);
}
}
받아와서 넣어주는 거니까 프로듀서다. 그러므로 extends 다.
package me.whiteship.chapter05.item31;
import me.whiteship.chapter05.item31.exmaple.Box;
import me.whiteship.chapter05.item31.exmaple.IntegerBox;
import java.util.ArrayList;
import java.util.List;
// 와일드카드 타입을 사용해 재귀적 타입 한정을 다듬었다. (187쪽)
public class RecursiveTypeBound {
public static <E extends Comparable<? super E>> E max(List<? extends E> list) {
if (list.isEmpty())
throw new IllegalArgumentException("빈 리스트");
E result = null;
for (E e : list)
if (result == null || e.compareTo(result) > 0)
result = e;
return result;
}
public static void main(String[] args) {
List<IntegerBox> list = new ArrayList<>();
list.add(new IntegerBox(10, "keesun"));
list.add(new IntegerBox(2, "whiteship"));
System.out.println(max(list));
}
}
package me.whiteship.chapter05.item31.exmaple;
public class IntegerBox extends Box<Integer> {
private final String message;
public IntegerBox(int value, String message) {
super(value);
this.message = message;
}
@Override
public String toString() {
return "IntegerBox{" +
"message='" + message + '\'' +
", value=" + value +
'}';
}
}
Comparable은 값을 꺼내서 비교하는 거니까 소비를 하는 것이다.
상위 타입만 Comparable을 구현하는 것이다.

E 는 IntegerBox, E ? super E 는 Box를 의미한다.

package me.whiteship.chapter05.item31;
import me.whiteship.chapter05.item31.exmaple.Box;
import me.whiteship.chapter05.item31.exmaple.IntegerBox;
import java.util.Arrays;
import java.util.List;
// 와일드카드 타입을 실제 타입으로 바꿔주는 private 도우미 메서드 (189쪽)
public class Swap {
public static <E> void swap(List<E> list, int i, int j) {
// public static void swap(List<?> list, int i, int j) {
list.set(i, list.set(j, list.get(i)));
// swapHelper(list, i, j);
}
// 와일드카드 타입을 실제 타입으로 바꿔주는 private 도우미 메서드
// 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(args);
swap(argList, 0, argList.size() - 1);
System.out.println(argList);
}
}

?는 타입을 모른다.
E는 타입을 안다.
package me.whiteship.chapter05.item31;
import me.whiteship.chapter05.item31.exmaple.Box;
import me.whiteship.chapter05.item31.exmaple.IntegerBox;
import java.util.Arrays;
import java.util.List;
// 와일드카드 타입을 실제 타입으로 바꿔주는 private 도우미 메서드 (189쪽)
public class Swap {
// public static <E> void swap(List<E> list, int i, int j) {
public static void swap(List<?> list, int i, int j) {
list.set(i, list.set(j, list.get(i)));
// swapHelper(list, i, j);
}
// 와일드카드 타입을 실제 타입으로 바꿔주는 private 도우미 메서드
// 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(args);
swap(argList, 0, argList.size() - 1);
System.out.println(argList);
Box<?> myBox = new IntegerBox(10, "keesun");
myBox.change(100);
}
}
?타입이라서 들어오는게 무슨 타입인지 모르겠어.


물음표는 pecs원칙을 따를 때만, extends나 super랑 함께 쓰는 걸로만 규칙을 정하는게 좋다.

package me.whiteship.chapter05.item31.typeinference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class BoxExample {
private static <U> void addBox(U u, List<Box<U>> boxes) {
Box<U> box = new Box<>();
box.set(u);
boxes.add(box);
}
private static <U> void outputBoxes(List<Box<U>> boxes) {
int counter = 0;
for (Box<U> box: boxes) {
U boxContents = box.get();
System.out.println("Box #" + counter + " contains [" +
boxContents.toString() + "]");
counter++;
}
}
private static void processStringList(List<String> stringList) {
}
public static void main(String[] args) {
ArrayList<Box<Integer>> listOfIntegerBoxes = new ArrayList<>();
BoxExample.addBox(10, listOfIntegerBoxes);
BoxExample.addBox(20, listOfIntegerBoxes);
BoxExample.addBox(30, listOfIntegerBoxes);
BoxExample.outputBoxes(listOfIntegerBoxes);
// Target Type
List<String> stringlist = Collections.emptyList();
List<Integer> integerlist = Collections.emptyList();
BoxExample.processStringList(Collections.emptyList());
}
}
type inference, 타입추론이 없었다면 <> 대신에 다 적어줘야 했다. <>를 쓰면 왼쪽에 이미 정의가 되어 있었기 떄문에 알아서 타입을 추론해 준다.
public static void main(String[] args) {
ArrayList<Box<Integer>> listOfIntegerBoxes = new ArrayList<>();
BoxExample.<Integer>addBox(10, listOfIntegerBoxes);
BoxExample.addBox(20, listOfIntegerBoxes);
BoxExample.addBox(30, listOfIntegerBoxes);
BoxExample.outputBoxes(listOfIntegerBoxes);
// Target Type
List<String> stringlist = Collections.emptyList();
List<Integer> integerlist = Collections.emptyList();
BoxExample.processStringList(Collections.emptyList());
}
이거를 타입 위트니스라고 한다. 명시적 타입 인수라고 한다. 대부분의 경우는 해당 변수를 줄여서 사용할 수 있다.
private static void processStringList(List<String> stringList)
매개변수에서 List<String>에 적혀있기 때문에 알아서 넣을 때 타입 추론이 된다.
public static void main(String[] args) {
var listOfIntegerBoxes = new ArrayList<Box<Integer>>();
BoxExample.addBox(10, listOfIntegerBoxes);
BoxExample.addBox(20, listOfIntegerBoxes);
BoxExample.addBox(30, listOfIntegerBoxes);
BoxExample.outputBoxes(listOfIntegerBoxes);
// Target Type
List<String> stringlist = Collections.emptyList();
List<Integer> integerlist = Collections.emptyList();
BoxExample.processStringList(Collections.emptyList());
}
var은 변수의 타입을 추론해주는 것이라 타입을 줄여 쓸 수 있는 방법 중 하나이다.