컴파일시 타입을 체크해 주는 기능
컴파일 할 때 타입 체크가 가능하지만 한계가 있어서 만들어진 개념
ArrayList는 Object 배열을 가지고 있어서 모든 종류의 객체를 저장할 수 있는데, 특정 객체만 저장하고 싶다면? 특정 객체 이외의 객체가 배열에 들어오면 어떻게 잡아내야 할까? -> Generics를 사용
//숫자만 넣고 싶은 ArrayList를 생성
ArrayList list = new ArrayList();
list.add(30);
list.add(20);
list.add("30");
//컴파일 시 잡아낼 수 없으며
//실행하면 형변환 에러 발생 ClassCastExecption(런타임예외)
//자식끼리 즉 형제 레벨 클래스끼리는 형변환 불가능
//Object -> Integer, String
Integer i = (Integer)list.get(2);
ArrayList<Integer> list2 = new ArrayList<Integer>();
list.add(30);
list.add(20);
list.add("30"); //컴파일 에러 발생
장점
1. 객체의 타입 안정성을 높인다.
2. 형변환을 생략할 수 있어 코드가 간결해 진다.
추가 정리 사항
위에서 발생한 ClassCastException은 런타임 예외이다. 런타임 예외는 프로그래머 실수로 발생하는 에러로 컴파일이 아닌 실행했을 때 발생하는 예외이다. 따라서 런타임 예외를 어떻게 하면 프로그램을 실행하기 전(컴파일 타임)에 체크해볼 수 있을까?라는 생각의 결과 중 하나가 지네릭스이다. 컴파일에게 타입 정보를 제공하여 컴파일 시 체크하도록 한 것이다.
지네릭스 외에도 String 타입의 객체 초기화시 null이 아닌 " "로 초기화 하는 것도 런타임 예외인 NullPointException을 방지하기 위한 것이다.
List<TV> list = new ArrayList<Tv>();
List<TV> list = new LinkedList<Tv>();
ArrrayList<Product> list = new ArrayList<Product>();
list.add(new Product());
list.add(new Tv());
//클래스2 또는 클래스2의 자손만 타입으로 지정 가능하다.
class 클래스명< T extends 클래스2> {
...
}
//인터페이스와 클래스를 동시에 쓰려면 콤마가 아닌 &를 사용
class 클래스명< T extends 클래스명(클래스) & 클래스명(인터페이스)> {
...
}
ArrayList<Product> list = new ArrayList<Tv>(); //타입 불일치 에러 발생
ArrayList<? extends Product> list = new ArrayList<Tv>();
ArrayList<? extends Product> list = new ArrayList<Audio>();
- '<? extends T>': T와 그 자손들만 가능
- '<? super T>': T와 그 조상들만 가능
- '<?>: 제한 없음. 모든 타입 가능 = '<? extends Object>
class FruitBox<T> { //지네릭 클래스
//지네릭 메서드
static <T> void sort(List<T> list, Comparator<? super T> c)
}
FruitBox<? extends Fruit> fbox = new FruitBox<Fruit>();
FruitBox<? extends Fruit> abox = new FruitBox<Apple>();
//(FruitBox<? extends Fruit>) new FruitBox<Apple>()와 같음
참고
책 자바의 정석