제한된 지네릭 클래스
- extends로 대입할 수 있는 타입을 제한
class FruitBox<T extends Fruit> { // Fruit의 자손만 타입으로 지정가능
ArrayList<T> list = new ArrayList<T>();
...
}
FruitBox<Apple> appleBox = new FruitBox<Apple>(); // OK
FruitBox<Toy> toyBox = new FruitBox<Toy>(); // 에러. Toy는 Fruit의 자손이 아님
- 인터페이스인 경우에도 extends 사용
interface Eatable { }
class FruitBox<T extends Eatable> { ... }
< 예제 >
지네릭스의 제약
- 타입 변수에 대입은 인스턴스 별로 다르게 가능
Box<Apple> appleBox = new Box<Apple>(); // OK. Apple 객체만 저장 가능
Box<Grape> grapeBox = new Box<Grape>(); // OK. Grape 객체만 저장 가능
제약
1. static 멤버에 타입 변수 사용 불가
class Box<T> {
static T item; // 에러
static int compare(T t1, T t2) { ... } // 에러
2. 배열 생성할 때 타입 변수 사용 불가. 타입 변수로 배열 선언은 가능
class Box<T> {
T[ ] itemArr; // OK. T타입의 배열을 위한 참조 변수
...
T[ ] toArray() {
T[ ] tmpArr = new T[itemArr.length]; // 에러. 지네릭 배열 생성 불가
...
➔ T : 객체 생성 불가 , 배열 생성 불가 ( new 연산자 다음에 T 사용할 수 없음)
와일드 카드 <?>
- 하나의 참조 변수로 대입된 타입이 다른 객체를 참조 가능
<? extends T> : 와일드 카드의 상한 제한. T와 그 자손들만 가능
<? super T> : 와일드 카드의 하한 제한. T와 그 조상들만 가능
<?> : 제한 없음. 모든 타입이 가능. <? extends Object>와 동일
ArrayList<? extends Product> list = new ArrayList<Tv>(); // OK (불일치 OK)
ArrayList<? extends Product> list = new ArrayList<Audio>(); // OK
ArrayList<Product> list = new ArrayList<Tv>(); // 에러. 대입된 타입 불일치
- 메서드의 매개변수에 와일드 카드를 사용
static Juice makeJuice(FruitBox<? extends Fruit> box) {
String tmp = "";
for(Fruit f : box.getList()) tmp += f + " ";
return new Juice(tmp);
}
< 예제 >
지네릭 메서드
지네릭 타입이 선언된 메서드 (타입 변수는 메서드 내에서만 유효)
static <T> void sort(List<T> list, Comparator<? super T> c)
- 클래스 타입 매개변수 <T>와 메서드의 타입 매개변수 <T>는 별개
class FruitBox<T> {
...
static <T> void sort(List<T> list, Comparator<? super T> c) {
...
}
}
- 메서드를 호출할 때마다 타입을 대입해야 함 (대부분 생략 가능)
- 메서드를 호출할 때 타입을 생략하지 않을 때는 클래스 이름 생략 불가
Sysytem.out.println(<Fruit>makeJuice(fruitBox)); // 에러. 클래스 이름 생략 불가
Sysytem.out.println(this.<Fruit>makeJuice(fruitBox)); // OK
Sysytem.out.println(Juicer.<Fruit>makeJuice(fruitBox)); // OK
와일드 카드 VS 지네릭 메서드
와일드 카드 : 하나의 참조변수로 서로 다른 타입이 대입된 여러 지네릭 객체를 다루기 위한 것
지네릭 메서드 : 메서드를 호출할 때마다 다른 지네릭 타입을 대입할 수 있게 한 것, 와일드 카드 안될 때도 사용
지네릭 타입의 형변환
- 지네릭 타입과 원시 타입 간의 형변환은 바람직 하지 않음 (경고 발생)
- 와일드 카드가 사용된 지네릭 타입으로는 형변환 가능
지네릭 타입의 제거
컴파일러는 지네릭 타입을 제거하고 필요한 곳에 형변환을 넣는다
( 지네릭스를 도입하면서 Object ➔ <T>로 바꿨는데 컴파일 후에는 다시 <T> ➔ Object 가 됨 )
1. 지네릭 타입의 경계(bound)를 제거
2. 지네릭 타입 제거 후에 타입이 불일치하면 형변환을 추가
3. 와일드 카드가 포함된 경우, 적절한 타입으로 형변환 추가
'JAVA > 자바의 정석' 카테고리의 다른 글
[자바의 정석 - 기초편] 12. (4) 애너테이션 ( 표준 / 메타 / 타입 / 요소 / 규칙 ) (0) | 2023.12.14 |
---|---|
[자바의 정석 - 기초편] 12. (3) 열거형 enum ( 조상 / 멤버 추가 ) (0) | 2023.12.14 |
[자바의 정석 - 기초편] 12. (1) 지네릭스 Generics ( 타입 변수 / 용어 / 타입 / 다형성 / Iterator<E> / HashMap<K,V> ) (0) | 2023.12.13 |
[자바의 정석 - 기초편] 11. (6) HashMap / Hashtable / Collections 컬렉션 클래스 요약 (0) | 2023.12.13 |
[자바의 정석 - 기초편] 11. (5) HashSet / TreeSet (0) | 2023.12.12 |