Sub[]
은 배열 Super[]
의 하위 타입이다 (공변 -> 함께 변하다)위의 주요 차이로 인해 배열과 제네릭은 잘 어울리지 못한다.
- 배열은 제네릭 타입, 매개변수화 타입, 타입 매개변수로 사용할 수 없다.
- 즉
new List<E>[]
,new List>String>[]
,new E[]
와 같이 작성하면 컴파일 할 때 제네릭 배열 생성 오류를 일으킨다.
// 제네릭 배열 생성을 허용하지 않는 이유 - 컴파일되지 않는다.
List<String>[] stringLists = new List<String>[1]; // (1)
List<Integer> intList = List.of(42); // (2)
Object[] objects = stringLists; // (3)
objects[0] = intList; // (4)
String s = stringLists[0].get(0); // (5)
List<Integer>
을 생성List<String>
의 배열을 Object 배열에 할당List<Integer>
의 인스턴스를 Ojbect 배열의 첫 원소로 저장한다.List<Integer>
인스턴스의 타입은 단순히 List
가 되고 List<Integer>[]
인스턴스의 타입은 List[]
가 된다.ClassCastException
이 발생한다.위에서 발생하는 일을 막으려면 (1)에서 컴파일 오류를 내야 한다.
컬렉션 안의 원소 중 하나를 무작위로 선택해 반환하는 메서드
- 주사위판, 매직 9볼, 몬테카를로 시뮬레이션용 데이터 소스 등으로 사용할 수 있다.
public class Chooser {
private final Object[] choiceArray;
public Chooser(Collection choices) {
choiceArray = choices.toArray();
}
public Object choose() {
Random rnd = ThreadLocalRandom.current();
return choiceArray[rnd.nextInt(choiceArray.length)];
}
}
// 컴파일 되지 않는다
public class Chooser<T> {
private final T[] choiceArray;
public Chooser(Collection<T> choices) {
choiceArray = choices.toArray();
}
// choose는 그대로다.
}
컴파일하면 choiceArray = choices.toArray();
부분에서
Object[] cannot be converted to T[]
에러가 난다.
choiceArray = (T[]) choices.toArray();
와 같이 Object 배열을 T 배열로 형변환 하면 해결 된다.
하지만 T가 무슨 타입인지 알 수 없으니 이 형변환이 런타임에도 안전한지 보장할 수 없다는 경고가 뜰 것이다.
비검사 형변환 경고를 제거하려면 배열 대신 리스트를 쓰면 된다.
publc class Chooser<T> {
private final List<T> choiceList;
public Chooser(Collection<T> choices) {
coiceList = new ArrayList<>(choices);
}
public T choose() {
Random rnd = ThreadLocalRandom.current();
return choiceList.get(rnd.nextInt(choiceList.size()));
}
}
코드양이 조금 늘었고 조금 더 느림
하지만 런타임에 ClassCastException
을 만날 일이 없으니 그만한 가치가 있다.