- 객체의 타입을 컴파일 시에 체크해 같은 타입이라는 것만 보장하고, 타입 안정성을 높인다.
- 타입 체크와 형변환을 생략가능하게 한다.
e.g) List에 타입을 명시하지 않으면 다양한 타입의 객체를 한 리스트에 담을 수 있는데, 이걸 하나로 제한한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
class Box { Object item; void setItem(Object item) { this.item = item; } Object getItem() { return item; } } // -- Generics class Box<T> { T item; void setItem(T item) { this.item = item }; T getItem() { return item; } }
타입
<T>
,ArrayList<E>
,Map<K, V>
등 다양한 타입 변수 네이밍을 사용한다.컴파일 타임에 체크한다. 위의 예시에서
Box<String> b = new Box<String>();
라고 선언하면, 타입T
가 컴파일시에String
으로 변환된다.static 멤버: 모든 객체에 대해 동일하게 동작해야한다. 따라서, static에는 지네릭스가 적용 불가능하다.
가능하면 Raw 타입을 사용하지 않고 지네릭을 사용한다.
List
->List<Object>
- JDK 1.5부터 지네릭스가 도입되어, 지네릭스를 사용할 것을 권장하지만 일단은 하위 호환을 위해 raw 타입을 허용해 놓은 것.
- raw 타입을 사용하면 타입 안정성을 잃는다.
용어
Box<T>
: Generic 클래스 “T Box” or “T의 Box”T
: 타입 변수. String으로 선언하게 되면 “매개변수화 된 타입”이 된다.Box
: 원시 타입, 로(Raw) 타입
상속 관계의 Generics
1
2
3
4
5
6
7
8
9
10
11
12
13
Box<Fruit> appleBox = new Box<Apple>(); // 에러
Box<Apple> appleBox = new FruitBox<Apple>(); // OK. 다형성
List<String> list = new ArrayList<String>(); // OK. 다형성
// ----
void add(Apple apple) { } // 이와같은 메서드가 있을 때
Box<Apple> appleBox = new Box<Apple>();
appleBox.add(new Apple()); // OK.
appleBox.add(new Grape()); // 에러. Apple 객체(혹은 자식 객체)만 추가 가능
appleBox.add(new GreenApple()); // OK. 자식 객체라면 가능
extends: 제한된 Generic
- 일반적인 지네릭스는 타입 안정성은 얻지만, 여전히 모든 타입이 가능하다.
<T extends Fruit>
과 같은 선언을 통해 특정 타입의 (Fruit 타입의) 자손들까지만 대입할 수 있도록 제한할 수 있다.- interface라도
implements
키워드 대신에extends
를 사용한다. - 클래스 Fruit의 자손이면서 Eatable 인터페이스도 구현해야한다면
<T extends Fruit & Eatable>
- interface라도
와일드 카드
static 메서드에는 지네릭을 사용할 수 없는데, 그렇다고 여러 타입의 매개변수를 갖도록 재선언하는 것은 중복을 만든다. 따라서 아래와 같이 선언
1 2 3
static Applebox add(FruitBox<? extends Fruit> box) { // .. }
<? extends T>
: T와 그 자손들만 가능<? super T>
: T와 그 조상들만 가능<?>
: 모든 타입 가능. ==<? extends Object>
Generic 메서드
: 메서드 한정 지역적 Generic 선언으로 복잡도를 낮춘다.
메서드 한정이므로 static 변수에도 사용 가능하다. 이펙티브 자바에서는 Generic 메소드가 사용이 가능한 부분이라면 무조건 이걸 사용하는 것을 권장한다.
1 2 3 4 5 6 7 8 9
static void printAll(ArrayList<? extends Product> list, ArrayList<? extends Product> list2) { // .. } // 위의 내용을 지네릭 메서드로 변환하면 static <T extends Product> void printAll(ArrayList<T> list, ArrayList<T> list2) { // .. }
Reference)
자바의 정석 3판