제네릭 프로그래밍다이아몬드 연산자 <><T extends 클래스> 사용하기한정적 와일드카드(bounded wildcard)비한정적 와일드카드(unbounded wildcard)WildCard제네릭 메서드Generic array 생성Generic Type 비교( T extends Comparable 이용)Recursive Type BoundComparable 인터페이스와 함께 사용시뮬레이트한 셀프 타입 관용구한정적 와일드카드를 이용한 변형
제네릭 프로그래밍
- 제네릭에 들어가는 임의 변수 자료형 T(E, K, ..)는 Object형태여야함(래퍼클래스로 넣어주어야함)
- int[] 같은 array들도 Object임

- 클래스에서 사용하는 변수의 자료형이 여러개 일 수 있고, 그 기능(메서드)은 동일한 경우 클래스의 자료형을 특정하지 않고 추후 해당 클래스를 사용할 때 지정할 수 있도록 선언
public class ThreeDPrinter1{ private Powder material; public void setMaterial(Powder material) { this.material = material; } public Powder getMaterial() { return material; } } //////////////////////////////////////////// public class ThreeDPrinter2{ private Plastic material; public void setMaterial(Plastic material) { this.material = material; } public Plastic getMaterial() { return material; } }
- 재료만 다르고 나머지는 완전똑같음
- 위 코드를 Genric을 이용하여 코딩
public class GenericPrinter<T> { private T material; public void setMaterial(T material) { this.material = material; } public T getMaterial() { return material; } public String toString(){ return material.toString(); } }
다이아몬드 연산자 <>
<T extends 클래스> 사용하기
- T 자료형의 범위를 제한하기
public class GenericPrinter<T extends Material>

매개변수 T에 대해 조건을 더 붙이고 싶을때 사용
한정적 와일드카드(bounded wildcard)
- Iterable<? extends E> → E 의 하위 타입의 Iterable
- extends라는 키워드는 이 상황에 딱 어울리지는 않음. 하위타입이란 자기 자신도 포함하지만, 그렇다고 자신을 확장한 것은 아니기에
- Collection<? super E> → E의 상위 타입의 Collection
- 모든 타입은 자기 자신의 상위 타입임.
매개변수 T에 대해 조건을 더 붙이는 것이 아닌, Iterable과 Collection 등 Object를 담을 수 있는 컨테이너의 요소의 범위를 지정할 때 사용
비한정적 와일드카드(unbounded wildcard)
- 제네릭 타입을 쓰고 싶지만 실제 타입 매개변수가 무엇인지 신경 쓰고 싶지 않다면 물음표(?)를 사용하자. 이것이 어떤 타입이라도 담을 수 있는 가장 범용적인 매개변수화 타입임
- 그러나 데이터를 넣는 동작을 하기 위해서는 타입을 명시해주어야 함. ?만으로 명시된 타입의 제네릭에 넣을 수 있는 값은 null 밖에 없음
- List<?> : 이렇게 되면 null 이외의 어떠한 값도 넣을 수 없음
- 데이터를 넣기 위해서는 와일드카드 타입을 실제 타입으로 바꿔주는 부분이 필요함
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))); }
- Map<Class<?>, Object> : 비한정적 와일드카드 타입이라 이 맵 안에 아무것도 넣을 수 없다고 생각할 수 있지만, 사실은 그 반대임. 와일드카드 타입이 nested로 있다는 점 때문에, 모든 키가 서로 다른 매개변수화 타입(parameterized type)일 수 있다는 뜻임
WildCard
- PECS : producer-extends, consumer-super — 와일드카드 타입을 사용하는 기본 원칙
제네릭 메서드
public static <T, V> double makeRectangle(Point<T,V> p1, Point<T,V> p2) { double left = ((Number)p1.getX()).doubleValue(); double right =((Number)p2.getX()).doubleValue(); double top = ((Number)p1.getY()).doubleValue(); double bottom = ((Number)p2.getY()).doubleValue(); double width = right - left; double height = bottom - top; return width * height; } double size = GenericMethod.<Integer, Double>makeRectangle(p1, p2);
- 타입 매개변수들을 선언하는 타입 매개변수 목록은 메서드의 제한자와 반환 타입 사이에 온다
Generic array 생성
class Heap<E>{ E[] array = new E[10]; // 이렇게 하면 에러가 남. E[] array = (E[]) new Object[10] // 이런식으로 정의해주어야함 E[] returnArr(){ return array } } Heap<
- 제너릭 타입은 java에 타입 안정성을 보장하기 위해 추가됐는데, 이를 위해 제너릭을 런타임에 오버헤드를 주는 것이 아닌, 컴파일러가 type erasure라는 과정을 수행하게 됨
- Type Erasure : type 형태를 다 삭제하고 그것을 Object 혹은 그것의 상위 클래스(extends시)로 대체 함 ⇒ 이러한 방식으로 컴파일을 한 다음에는 정상적인 형태의 코드만 남게 되는 것임
- 그래서 Object형태의 코드로 런타임 시에 돌아가게 되는데,
E[] array = new E[10]
⇒Object[] array = new Object[10]
으로 바뀌게 되고 런타임에서Heap<Integer>
사용 시 array 가 Object 타입을 암묵적으로 Integer 바꾸어야 되는 상황이 발생하기에 에러가 나는 것임
public <T> List<T> genericMethod(List<T> list) { return list.stream().collect(Collectors.toList()); } // ==> // for illustration public List<Object> withErasure(List<Object> list) { return list.stream().collect(Collectors.toList()); } // which in practice results in public List withErasure(List list) { return list.stream().collect(Collectors.toList()); }
public <T extends Building> void genericMethod(T t) { ... } // ==> public void genericMethod(Building t) { ... }
Generic Type 비교( T extends Comparable 이용)
class Element<T extends Comparable<T>>
Recursive Type Bound
Comparable 인터페이스와 함께 사용

- 의미적으로 보면 T 끼리는 비교가 가능하다 라는 것을 의미하는 것임
- List는 모든 요소들이 비교가 가능해야 하기 때문에, List가 갖고 있는 요소의 타입 T는 Comparable<T>를 갖고 있어야(비교가 가능해야) 한다는 것임
- T가 Comparable<T>를 확장한다. Comparable<T> 는 T 인스턴스를 소비한다 (그리고 선후 관계를 뜻하는 정수를 생산한다)
시뮬레이트한 셀프 타입 관용구
public abstract class Pizza{ abstract static class Builder<T extends Builder<T>> { protected abstract T self(); } } public class NyPizza extends Pizza{ public static class Builder extends Pizza.Builder<Builder>{ @Override protected Builder self() { return this; } } }
- 여기서는 T가 Builder인 T라는 것. T가 Builder의 성질(Build할 수 있는)을 갖고 있다는 것임
- Pizza 의 <T extends Builder<T>> 에서의 Builder는 Pizza.Builder
- NyPizza의 Builder extends Pizza.Builder<Builder> 에서의 제일 마지막 Builder는 NyPizza.Builder인 것임
한정적 와일드카드를 이용한 변형
public static <E extends Comparable<? super E>> E max(List<? extends E> list)