제네릭은 불공변:함께 변하지않는다
런타임실패
Object [] objectArray= new Long[1];
ObjectArray[0]="타입이 달라 넣을수없다";
컴파일 실패
List<Object> objectList = new ArrayList<>(); // 호환되지 않는 타입이다.
objectList.add("타입이 달라 넣을 수 없다.");
배열은 자신이 담기로한 원소의 타입을 인지하고 확인
Long타입 배열에 String타입 데이터를 입력하면 ArrayStoreException이 발생
반면, 리스트는 타입 정보가 런타임에는 소거된다.
이런 이유로 배열은 제네릭 타입(new List[]), 매개변수화 타입(new List[]), 타입 매개변수(new E[]) 로 사용할 수 없다. 이런 식으로 코드를 작성하려하면 제네릭 배열 생성 오류를 일으킨다.
[제네릭 배열 생성을 허용하지 않는 이유 - 컴파일되지 않는다.]
제네릭 배열을 만들지 못하게하는 이유는 컴파일러가 자동 생성한 형변환 코드에서 런타임에 ClassCastException이 발생할 수 있기 때문에 타입 안전하지 않기 때문
E, List, List 같은 타입을 실체화 불가 타입이라고 한다.
실체화 되지 않아서 런타임에는 컴파일보다 타입정보를 작게 가지는 타입
실체화될 수 있는 타입은 List, Map 같은 비한정적 와일드카드 타입
@SafeVarargs
@SafeVarargs는 메서드 작성자가 해당 메서드가 타입 안전하다는 것을 보장하는 장치이다.
배열로 형변환할때 제네릭 배열 생성오류나 비검사 형변환 경고가 뜨는경우에
배열 E[]대신 List를 사용하면 해결된다.
public class Chooser {
private final Object[] choiceArray;
public Chooser(final Object[] choiceArray) {
this.choiceArray = choiceArray;
}
public Object choose(){
Random random = ThreadLocalRandom.current();
return choiceArray[random.nextInt(choiceArray.length)];
}
}
위 클래스를 사용하려면 choose 메서드를 호출할 때마다 반환된 Object를 원하는 타입으로 형변환해야 한다. 만약 타입이 다른 원소가 들어있으면 런타임시에 형변환 오류가 발생한다.
public class ListChooser {
private final List<T> choiceList;
public ListChooser(final Collection<T> choices) {
this.choiceList = new ArrayList<>(choices);
}
public T choose(){
Random random = ThreadLocalRandom.current();
return choiceList[random.nextInt(choiceList.size())];
}
}
리스트를 사용함으로써 런타임에 ClassCastException을 만날일이 없어짐