제네릭과 와일드카드
Generic
- 타입을 매개변수화한 것
- 클래스 혹은 메소드에 선언 가능
- 동시에 여러 타입 선언 가능
A Simple Box Class
public class Box {
private Object object;
public void set(Object object) {
this.object = object;
}
public Object get() {
return object;
}
}
- 컴파일 시 클래스가 어떻게 사용되는지 확인할 방법이 없다.
- 타입 안정성 부족
Box box = new Box();
box.set(123); // Integer 값을 box에 저장
box.set("hello"); // 실수로 String 값을 box에 저장
Integer value = (Integer) box.get(); // 여기서 ClassCastException 발생
A Generic Version of the Box Class
public class Box<T> {
// T stands for "Type"
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}
- 모든 Object를 T로 변경
- 파라미터 타입이나 리턴 타입에 대한 정의를 외부로 미룬다.
- 타입에 대해 유연성과 안정성을 확보
- 타입을 유연하게 처리하며, 런타임에 발생할 수 있는 타입에러를 컴파일 전에 검출
-
제네릭 파라미터에 의해 타입이 고정되기 때문에 안정성 확보
static멤버에 타입 변수 T를 사용할 수 없다. T는 인스턴스 변수로 간주되기 때문이다.
class Box<T> {
static T item; // Error!
static int compare(T t1, T t2) {} // Error!
}
- 제네릭 타입의 배열을 생성하는 것이 허용되지 않는다.
class Box<T> {
T[] items; // OK
T[] items = new T[3]; // Error!
}
Wildcard
- <?> 이것의 명칭을 와일드카드라 한다.
- 어떤 타입이든 될 수 있다는 뜻
- 제네릭의 한계를 보완한 것이라 생각하면 이해가 쉽다.
<?>- 제한 없음. 모든 타입 가능
<? extends Object>와 동일
<? extends T>- 와일드카드의 상한 제한
- T와 그 자손들만 가능
<? super T>- 와일드카드의 하한 제한
- T와 그 조상들만 가능
Generic method
class FruitBox<T> {
static <T> void sort(List<T> list, Comparator<? super T> c) {}
}
- 메서드의 선언부에 제네릭 타입이 선언된 메서드를 제네릭 메서드라 한다.
- 제네릭 클래스에 정의된 타입 매개변수와 제네릭 메서드에 정의된 타입 매개변수는 완전 별개의 것이다.
- 문자 T만 같은 것을 쓰는 것이지 같은 타입은 아니다.
- 메서드에 선언된 제네릭 타입은 지역 변수를 선언한 것과 같다고 생각하면 된다.
- 매개변수에 선언할 타입 T에 대한 자료형을 미리 선언해 주기 때문에 static 메서드에서 타입 매개변수 T의 사용을 가능하게 해 주는 것이다.
Collections의 sort() 메서드
public static <T extends Comparable<? super T>> void sort(List<T> list)
- 타입 T를 요소로 하는 매개변수를 허용하되
- T는
Comparable을 구현한 클래스여야 하며 T 또는 그 조상의 타입을 비교하는Comparable이어야 한다. - 만약 T가
Student이고Person의 자손이라면<? super T>는Student,Person,Object가 가능하다.