class Box {
Object item;
void setItem(Object item) { this.item = item; }
Object getItem() { return item; }
}
이 클래스를 제네릭 클래스로 변경하면 클래스옆에 <T>
를 붙이면 된다. 그리고 Object
를 모두 T
로 바꾼다.
class Box<T> {
T item;
void setItem(T item) { this.item = item; }
T getItem() { return item; }
}
Box 에서 T를 타입변수
라고하며, 타입 변수는 T가 아닌 다른 것을 사용해도 된다. 아래표와 같은 타입변수가 주로 사용된다.
제네릭은 컴파일시 타입을 체크해 준다고 했다.
Box<String> b = new Box<String>(); //타입 T 대신, 실제 타입 String을 지정
b.setItem(new Object()); // 에러발생 제네릭이 String이므로 String타입만 가능
b.setItem("ABC"); // String 타입이므로 가능
String item = b.getIgem(); // 제네릭의 다른 장점으로 형변환 생략가능
Box<Apple> appleBox = new Box<Apple>(); Apple객체만 저장이 가능
Box<Grape> appleBox = new Box<Grape>(); Grape객체만 저장이 가능
하지만 모든 객체에 대해 동일하게 동작하는 static멤버에는 타입 변수 T를 사용할 수 없다. T는 인스턴스 변수로 간주.(static멤버에 인스턴스 변수는 사용불가)
class Box<T> {
static T item; //에러발생
static int compare(T t1,T t2){...} //에러발생
}
class Box<T> {
T[] itemArr;
T[] toArray() {
T[] tmpArr = new T[imemArr.length]; //에러. 제네릭 배열 생성불가
}
}
여기서 배열을 생성할 수 없는 것은 new연산자 때문이다. 이 연산자는 컴파일 시점에 타입 T가 뭔지 알아야 하지만 컴파일 시점에는 어떤
타입이 될지 알 수 없다.
##제네릭 클래스의 객체 생성과 사용
Box<Apple> applebox = new Box<Grape>()
같이 타입이 일치하지 않으면 에러가 발생한다.Box<Apple> appleBox = new FruitBox<Apple>();
은 다형성이 적용되서 오류가 발생하지 않는다.Box<Apple> appleBox = new Box<>();
extends
라는 키워드를 사용한다. 로 설정 시에는 숫자 관련된 래퍼 클래스와 그 자손들만제네릭 타입이 다른 것만으로는 오버로딩이 성립하지 않기 때문에 와일드 카드
가 고안됐다. 와일드 카드?
로 표현하는데, 와일드 카드는 어떠한 타입도 될 수 있다. <?>만으로는 Object타입과 다를 게 없으므로, extends
와super
로
상한과 하흔을 제한할 수있다.
<? extends T> 와일드 카드의 상한 제한. T와 그 자손들만 가능
< ? Super T> 와일드 카드이 하한 제한. T와 그 조상들만 가능
< ? > 제한 없음. 모든 타입이 가능 < ? extends Object >와 동일
< K extends T>, <? extends T>는 차이가 있다 K는 특정 타입으로 지정이 되지만, ?는 타입이 지정되지 않는다는 의미가 있다.
static Juice makeJuice(FruitBox<? extends Fruit> box) {}
를 지네릭 메서드로 바꾸면static <T extends Fruit> Juice makeJuice(FruitBox<T> box) {}
로 바꿀수 있다public static <T extends Comparable<? super T>> void sort(List<T> list)
메서드를 보자면 List의 요소가 Comparable을T
또는 T의조상
이 올수 있다는것이다. T
가 Orange
이고 조상인 Fruit
가 있다면 T
에는 Orange
Fruit
Object
가 올수있다.참고문헌 : 자바의 정석 3rd