배열은 공변 (covariant), 제네릭은 불공변
상속 관계에서 생각해보면 → 공변 : 같이 변한다. / 불공변 : 같이 변하지 않는다.
public class IntegerToString {
public static void main(String[] args) {
// 공변
Object[] anything = new String[10];
anything[0] = 1;
// 불공변
List<String> names = new ArrayList<>();
// List<Object> objects = names;
// // 제네릭과 배열을 같이 사용할 수 있다면...
// List<String>[] stringLists = new ArrayList<String>[1];
// List<Integer> intList = List.of(42);
// Object[] objects = stringLists;
// objects[0] = intList;
// String s = stringLists[0].get(0);
// System.out.println(s);
}
}
배열은 실체화(reify) 되지만, 제네릭은 실체화 되지 않는다. (소거)
new Generic<타입>[배열] 은 컴파일 할 수 없다.
제네릭 소거: 원소의 타입을 컴파일 타임에만 검사하며 런타임에는 알 수 없다
// 코드 28-6 배열 기반 Chooser
public class Chooser_Array {
private final Object[] choiceList;
public Chooser_Array(Collection choices) {
choiceList = choices.toArray();
}
public Object choose() {
Random rnd = ThreadLocalRandom.current();
return choiceList[rnd.nextInt(choiceList.length)];
}
public static void main(String[] args) {
List<Integer> intList = List.of(1, 2, 3, 4, 5, 6);
Chooser_Array chooser = new Chooser_Array(intList);
for (int i = 0; i < 10; i++) {
Number choice = (Number) chooser.choose();
System.out.println(choice);
}
}
}
---
// 코드 28-6 리스트 기반 Chooser - 타입 안전성 확보! (168쪽)
public class Chooser<T> {
private final List<T> choiceList;
public Chooser(Collection<T> choices) {
choiceList = new ArrayList<>(choices);
}
public T choose() {
Random rnd = ThreadLocalRandom.current();
return choiceList.get(rnd.nextInt(choiceList.size()));
}
public static void main(String[] args) {
List<Integer> intList = List.of(1, 2, 3, 4, 5, 6);
Chooser<Integer> chooser = new Chooser<>(intList);
for (int i = 0; i < 10; i++) {
Number choice = chooser.choose();
System.out.println(choice);
}
}
}
생성자와 메서드의 제네릭 가변인자에 사용할 수 있는 애노테이션
public class SafeVaragsExample {
// @SafeVarargs // Not actually safe!
static void notSafe(List<String>... stringLists) {
Object[] array = stringLists; // List<String>... => List[], 그리고 배열은 공변이니까.
List<Integer> tmpList = List.of(42);
array[0] = tmpList; // Semantically invalid, but compiles without warnings
String s = stringLists[0].get(0); // Oh no, ClassCastException at runtime!
}
@SafeVarargs
static <T> void safe(T... values) {
for (T value: values) {
System.out.println(value);
}
// not return && not assign
}
public static void main(String[] args) {
SafeVaragsExample.safe("a", "b", "c");
SafeVaragsExample.notSafe(List.of("a", "b", "c"));
}
}