다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일 시의 타입 체크를 해주는 기능
장점
1. 타입 안정성을 제공
2. 타입체크와 형변환을 생략할 수 있어 코드가 간결해짐
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; }
}
T : 타입 변수, T가 아닌 다른 문자 사용 가능
타입 변수가 여러개인 경우 <K, V> 이렇게 콤마로 나열하면 된다.
이후 지네릭 클래스 객체를 생성할 때 T 대신 사용할 타입을 지정해줘야함
Box<String> b = new Box<String>();
b.setItem(new Object()); // String 타입이 아니므로 에러
b.setItem("ABC");
String item = b.getItem(); // 형변환이 필요없음
클래스에 String 만 가능하도록 하려면 클래스 정의에서 T 대신에 String 을 넣어주면 된다.
지네릭 타입은 컴파일할 때 검사이후 제거된다.
static멤버는 타입 변수를 사용할 수 없다.
지네릭 타입의 배열은 선언하는 것은 가능하지만 생성하는 것은 안된다.
new 연산자는 컴파일 시점에서 T가 어떤 타입인지 정확하게 알아야 하는데 불가능하기 때문
지네릭 배열을 꼭 생성해야 할 경우 newInstance() 와 같이 동적으로 객체를 생성하는 메서드로 배열을 생성하거나, Object 배열을 생성해서 복사한다음 T[]로 형변환 하는 방법등을 사용한다.
class FruitBox<T extends Fruit> {
}
지네릭 타입에 extends를 사용하면 특정 타입의 자손들만 대입할 수 있도록 제한할 수 있다.
class FruitBox<T extends Fruit & Eatable> {
}
인터페이스를 구현해야하는 경우도 extends를 쓰고
위와 같이 사용하면 Fruit의 자손이면서 Eatable을 구현한 클래스만 사용가능하도록 제한할 수 있다.
< ? extends T> : T와 그 자손들만 가능
< ? super T> : T와 그 조상들만 가능
< ?> : 모든 타입이 가능
매개변수로 지네릭 클래스를 받는 경우 지네릭 타입을 다양하게 받기 위해서 오버로딩을 하게되면 메서드 중복 정의 에러가 된다.
지네틱 타입은 컴파일 이후 사라지기 때문에 지네릭 타입이 다른 것 만으로는 오버로딩이 성립되지 않는다.
이때 와일드 카드를 사용해주면 된다.
메서드의 반환 타입 바로 앞에 < T>를 붙여 주어 지네릭 메서드를 만들수 있다.
static <T> void sort(List<T> list, Comparator<? super T> c)
지네릭 클래스 내부에서 지네릭 메서드가 선언된 경우 같은 T를 사용해도 지네릭 메서드의 T와는 별개다
<Fruit>sort(list, c);
지네릭 메서드를 호출할 때는 앞에 타입을 명시해줘야한다.
그러나 대부분 컴파일러가 타입을 추정할 수 있기 때문에 생략해도 된다.
지네릭 메서드를 호출할 때는 this. 이나 클래스명. 은 생략 불가
지네릭 타입과 원시타입간의 형변환 가능
대입된 타입이 다른 지네릭 타입 간에 형변환 불가
와일드 카드가 포함된 지네릭 타입으로 형변환은 가능