class Box<T> { // 제네릭 타입 변수 T 선언
T item;
void setItem(T item) { this.item = item; }
T getItem() { return item; }
}
// 사용
Box<String> b = new Box<>(); // 생성 시 실제 타입 지정
// 용어
// Box<T> : 제네릭 클래스
// T : 타입 변수 (의미있는 문자 권장: E, K, V 등)
// Box : raw type(원시 타입)
T는 정적 컨텍스트에서 쓸 수 없음 → static 멤버에 T 사용 불가.new 시점에 T의 구체 타입을 모르므로 제네릭 배열 직접 생성 불가 (new T[10] 금지).class FruitBox<T extends Fruit> { ... } // 클래스 상한
interface Eatable {}
class FruitBox2<T extends Fruit & Eatable> { } // 다중 경계는 & 사용
// 인터페이스 경계도 extends 키워드 사용
<? extends T> // 상한: T와 그 자손 (읽기 위주)
<? super T> // 하한: T와 그 조상 (쓰기 위주)
<?> // 무제한: ? == ? extends Object
와일드카드는 & 결합(다중 경계) 불가.
제네릭 오버로딩 충돌(타입 소거로 시그니처 동일) 상황에서, 파라미터에 와일드카드 사용으로 하나로 통일 가능.
static Juice makeJuice(FruitBox<? extends Fruit> box) { ... }
static <T extends Fruit> Juice makeJuice(FruitBox<T> box) { ... }
// 호출 시 타입 인수 명시 가능: Juicer.<Apple>makeJuice(appleBox);
// 대부분 타입 추론으로 생략 가능