12.1.1 지네릭스란?
- 다양한 타입의 객체를 다루는 메서드나 컬렉션 클래스에 컴파일 시의 타입 체크를 해주는 기능임
- 장점
- 타입 안정성을 제공함
- 타입체크와 형변환을 생략할 수 있으므로 코드가 간결해짐
12.1.2 지네릭 클래스의 선언
class Box<T>{
T item;
void setItem(T item){this.item = item;}
T getItem() {return item;}
}
Box<T>
에서 T를 타입 변수(Type Variable)라 함- 💡 T가 아닌 다른 것을 사용할 수 있으나, 상황에 맞게 의미있는 문자를 선택해야 함
- 타입 변수는 임의의 참조형 타입을 의미함
지네릭이 도입되기 이전의 코드와 호환을 위해 지내릭 클래스인데도 예전 방식으로 객체를 생성하는 것이 허용됨
Box b = new Box(); b.setItem("abc"); // 경고. unchecked or unsafe operation
Box<String> b = new Box()<String>;
b.setItem("abc");
💡 지네릭 타입을 지정하지 않으면 안전하지 않다는 경고가 발생함. 때문에 반드시 형을 지정하는 것이 좋음
지네릭스의 용어
/*
Box<T> : 지네릭 클래스. 'T의 Box' 또는 'T Box'라 읽음
T : 타입 변수 또는 타입 매개변수(T는 타입 문자)
Box : 원시 타입(Raw Type)
*/
Clss Box<T>{}
지네릭스는 모든 객체에 동일하게 동작해야 하므로 static 멤버에 사용할 수 없음
static T item; // 불가
static int compare(T t1, T t2) {...} // 불가
지네릭스 배열을 new
연산자로 생성할 수 없음
12.1.3 지네릭 클래스의 객체 생성과 사용
참조 변수와 생성자에 타입 변수가 일치해야 함
Box<Apple> appleBox = new Box<Apple>(); // 가능
/*
불가능
상속 관계여도 불가능
Fruit는 Apple의 부모임
*/
Box<Fruit> appleBox = new Box<Apple>();
/*
불가능
단, 두 지네릭 클래스의 타입이 상속관계에 있고, 타입 변수가 같은 경우는 가능
*/
Box<Apple> appleBox = new Box<Graph>();
Box<Apple> appleBox = new FruitBox<Apple>(); // 가능
타입 변수와 다른 타입의 객체는 추가할 수 없음
Box<Apple> appleBox = new Box<Apple>();
appleBox.add(new Apple()); // 가능
appleBox.add(new Grape()); // 불가능. 타입 변수의 자손은 가능함
Box<Fruit> appleBox = new Box<Fruit>();
appleBox.add(new Fruit()); // 가능
appleBox.add(new Apple()); // 가능
12.1.4 제한된 지네릭 클래스
타입의 종류를 제한할 수 있음
class FruitBox<T extends Fruit>{...} // Fruit의 자손만 타입으로 지정가능
인터페이스 구현에도 extends
를 사용함
interface Eatable{...} class FruitBox<T extends Eatable>{...}
여러 개의 클래스를 동시에 구현해야 하면 &
을 사용함
class FruitBox<T extends Fruit & Eatable> {...}
12.1.5 와일드 카드
- 기호는
?
으로 어떠한 타입도 될 수 있음 ?
만으로는Object
타입과 다를 게 없으므로, 상한과 하한을 제한할 수 있음
<? extends T> // 와일드 카드의 상한 제한. T와 그 자손들만 가능
<? super T> // 와일드 카드의 하한 제한. T와 그 조상들만 가능
<?> // 제한 없음. 모든 타입이 가능함. <? extends Object>와 동일
12.1.6 지네릭 메서드
- 메서드의 선언부에 지네릭 타입이 선언된 메서드
static <T> void sort(List<T> list, Comparator<? super T> c){...}
12.1.7 지네릭 타입의 형변환
지네릭 타입과 넌지네릭 타입간의 형변환은 항상 가능함
Box box = null; Box<Object> objBox = null; box = (Box)objbox; // 지네릭 -> 원시
objBox = (Box<Object>)box; // 원시 -> 지네릭
타입 변수가 다르면 불가능함
Box<Object> objbox = null;
Box<String> strBox = null;
objbox = (Box<String>)strBox; // 에러
strBox = (Box<Object>)box; // 에러
와일드 카드를 이용하면 가능함
Box<? extends Object> wBox = new Box<String>();
12.1.8 지네릭 타입의 제거
컴파일러는 지네릭 타입을 이용해 소스파일을 체크하여 필요한 곳에 형변환을 넣어주고 지네릭 타입을 제거함
즉, 컴파일된 파일(.class)에는 지네릭 타입에 대한 정보가 없음
이렇게 하는 이유는 이전의 소스 코드와 호환성을 유지하기 위해서임
제거 순서
1. 지네릭 타입의 경계(Bound)를 제거함
[*.java]
class Box<T extends Fruit>{ void add(T t){...} }
[*.class]
class Box{ void add(Fruit t){...} }
2. 지네릭 타입을 제거한 후에 타입이 일치하지 않으면 형변환을 추가함
[*.java]
T get(int i){ return list.get(i); }
[*.class]
Fruit get(int i){ return (Fruit)list.get(i); }
'자바의 정석 정리' 카테고리의 다른 글
자바의 정석 - 12.3 애너테이션(annotation) (0) | 2022.09.06 |
---|---|
자바의 정석 - 12.2 열거형(Enums) (0) | 2022.09.06 |
자바의 정석 - 11.13 Collections (0) | 2022.09.05 |
자바의 정석 - 11.12 Properties (0) | 2022.09.05 |
자바의 정석 - 11.11 TreeMap (0) | 2022.09.05 |