원시 타입으로 제네릭 타입을 인스턴스화할 수 없음
다음과 같은 매개변수화된 타입을 생각해 보세요:
class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
// ...
}Pair 객체를 생성할 때 타입 매개변수 K 또는 V를 기본 타입으로 대체할 수 없습니다:
Pair<int, char> p = new Pair<>(8, 'a'); // compile-time error타입 매개변수 K와 V는 프리미티브가 아닌 타입으로만 대체할 수 있습니다:
Pair<Integer, Character> p = new Pair<>(8, 'a');Java 컴파일러는 8을 Integer.valueOf(8)로, 'a'를 Character('a')로 자동 변경합니다:
Pair<Integer, Character> p = new Pair<>(Integer.valueOf(8), new Character('a'));오토박싱에 대한 자세한 내용은 숫자 및 문자열 섹션의 오토박싱 및 언박싱을 참조하세요.
타입 매개변수의 인스턴스를 만들 수 없음
타입 매개변수의 인스턴스를 만들 수 없습니다. 예를 들어 다음 코드는 컴파일 타임 오류를 유발합니다:
public static <E> void append(List<E> list) {
E elem = new E(); // compile-time error
list.add(elem);
}해결 방법으로 리플렉션을 통해 타입 파라미터의 객체를 생성할 수 있습니다:
public static <E> void append(List<E> list, Class<E> cls) throws Exception {
E elem = cls.newInstance(); // OK
list.add(elem);
}다음과 같이 append() 메서드를 호출할 수 있습니다:
List<String> ls = new ArrayList<>();
append(ls, String.class);
타입이 타입 매개변수인 정적 필드를 선언할 수 없음
클래스의 정적 필드는 클래스의 모든 정적이 아닌 객체가 공유하는 클래스 수준 변수입니다. 따라서 타입 매개변수인 정적 필드는 허용되지 않습니다. 다음 클래스를 생각해 보세요:
public class MobileDevice<T> {
private static T os;
// ...
}매개변수 타입의 정적 필드가 허용되면 다음 코드가 혼동될 수 있습니다:
MobileDevice<Smartphone> phone = new MobileDevice<>();
MobileDevice<Pager> pager = new MobileDevice<>();
MobileDevice<TabletPC> pc = new MobileDevice<>();정적 필드 os는 phone, pager, pc가 공유하는데, 실제 os의 타입은 무엇인가요? Smartphone, Pager, TabletPC가 동시에 될 수 없습니다. 따라서 타입 파라미터의 정적 필드를 만들 수 없습니다.
매개변수화된 타입과 함께 캐스트 또는 인스턴스 오브 사용 불가
Java 컴파일러는 제네릭 코드에서 모든 타입 매개변수를 지우기 때문에 런타임에 제네릭 타입에 대해 어떤 매개변수화된 타입이 사용되고 있는지 확인할 수 없습니다:
public static <E> void rtti(List<E> list) {
if (list instanceof ArrayList<Integer>) { // compile-time error
// ...
}
}rtti() 메서드에 전달되는 매개변수화된 타입의 집합은 다음과 같습니다:
S = { ArrayList<Integer>, ArrayList<String> LinkedList<Character>, ... }런타임은 타입 매개변수를 추적하지 않으므로 ArrayList<Integer>와 ArrayList<String>의 차이를 구분할 수 없습니다. 할 수 있는 최선은 제한되지 않은 와일드카드를 사용하여 목록이 ArrayList인지 확인하는 것입니다:
public static void rtti(List<?> list) {
if (list instanceof ArrayList<?>) { // OK; instanceof requires a reifiable type
// ...
}
}일반적으로 매개변수화된 타입은 바인딩되지 않은 와일드카드로 매개변수화되지 않는 한 형변환할 수 없습니다. 예를 들어
List<Integer> li = new ArrayList<>();
List<Number> ln = (List<Number>) li; // compile-time error그러나 어떤 경우에는 컴파일러가 타입 매개변수가 항상 유효하다는 것을 알고 형변환을 허용합니다. 예를 들어
List<String> l1 = ...;
ArrayList<String> l2 = (ArrayList<String>)l1; // OK
매개변수화된 타입의 배열을 만들 수 없음
매개변수화된 타입의 배열은 만들 수 없습니다. 예를 들어 다음 코드는 컴파일되지 않습니다:
List<Integer>[] arrayOfLists = new List<Integer>[2]; // compile-time error다음 코드는 배열에 서로 다른 타입을 삽입할 때 어떤 일이 발생하는지 보여줍니다:
Object[] strings = new String[2];
strings[0] = "hi"; // OK
strings[1] = 100; // An ArrayStoreException is thrown.제네릭 목록으로 동일한 작업을 시도하면 문제가 발생할 수 있습니다:
Object[] stringLists = new List<String>[2]; // compiler error, but pretend it's allowed
stringLists[0] = new ArrayList<String>(); // OK
stringLists[1] = new ArrayList<Integer>(); // An ArrayStoreException should be thrown,
// but the runtime can't detect it.매개변수화된 목록의 배열이 허용되면 이전 코드에서는 원하는 ArrayStoreException을 던지지 못합니다.
매개변수화된 타입의 객체를 생성, 캐치 또는 던질 수 없습니다.
제네릭 클래스는 Throwable 클래스를 직접 또는 간접적으로 확장할 수 없습니다. 예를 들어, 다음 클래스는 컴파일되지 않습니다:
// Extends Throwable indirectly
class MathException<T> extends Exception { /* ... */ } // compile-time error
// Extends Throwable directly
class QueueFullException<T> extends Throwable { /* ... */ // compile-time error메서드는 타입 매개변수의 인스턴스를 잡을 수 없습니다:
public static <T extends Exception, J> void execute(List<J> jobs) {
try {
for (J job : jobs)
// ...
} catch (T e) { // compile-time error
// ...
}
}그러나 throws 절에 타입 매개변수를 사용할 수 있습니다:
class Parser<T extends Exception> {
public void parse(File file) throws T { // OK
// ...
}
}
각 오버로드의 공식 파라미터 타입이 동일한 원시 타입으로 지워지는 메서드에 오버로드할 수 없습니다.
클래스에는 타입 지우기 후 동일한 서명을 갖는 두 개의 오버로드된 메서드를 가질 수 없습니다.
public class Example {
public void print(Set<String> strSet) { }
public void print(Set<Integer> intSet) { }
}오버로드는 모두 동일한 클래스 파일 표현을 공유하며 컴파일 타임 오류를 생성합니다.