제네릭 타입의 모든 타입 파라미터를 Bounds로 바꾸거나 타입 파라미터가 제한되지 않은 경우 Object로 바꿉니다. 따라서 생성된 바이트코드에는 일반 클래스, 인터페이스 및 메서드만 포함됩니다.
타입 안전을 유지하기 위해 필요한 경우 타입 캐스트를 삽입하십시오. 확장된 제네릭 타입에서 다형성을 유지하기 위해 브리지 메서드를 생성합니다.
타입 소거는 파라미터화된 타입에 대해 새 클래스가 생성되지 않도록 합니다. 결과적으로 제네릭은 런타임 오버헤드를 발생시키지 않습니다.
단일 연결 리스트의 노드를 나타내는 다음 일반 클래스를 고려하십시오.
public class Node<T> {
private T data;
private Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public T getData() { return data; }
// ...
}
타입 파라미터 T는 제한(bounds)이 없기 때문에 Java 컴파일러는 이를 Object로 바꿉니다. (Node<T> -> Object)
public class Node {
private Object data;
private Node next;
public Node(Object data, Node next) {
this.data = data;
this.next = next;
}
public Object getData() { return data; }
// ...
}
다음 예제에서 일반 Node 클래스는 제한된 타입 파라미터를 사용합니다.
public class Node<T extends Comparable<T>> {
private T data;
private Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public T getData() { return data; }
// ...
}
Java 컴파일러는 경계 타입 파라미터 T를 첫 번째 경계 클래스인 Comparable로 바꿉니다.
public class Node {
private Comparable data;
private Node next;
public Node(Comparable data, Node next) {
this.data = data;
this.next = next;
}
public Comparable getData() { return data; }
// ...
}
다음과 같은 일반적인 방법을 고려하십시오.
// Counts the number of occurrences of elem in anArray.
//
public static <T> int count(T[] anArray, T elem) {
int cnt = 0;
for (T e : anArray)
if (e.equals(elem))
++cnt;
return cnt;
}
타입 파라미터 T는 제한(bounds)이 없기 때문에 Java 컴파일러는 이를 Object로 바꿉니다.
public static int count(Object[] anArray, Object elem) {
int cnt = 0;
for (Object e : anArray)
if (e.equals(elem))
++cnt;
return cnt;
}
다음 클래스가 정의되어 있다고 가정합니다.
class Shape { /* ... */ }
class Circle extends Shape { /* ... */ }
class Rectangle extends Shape { /* ... */ }
다른 Shape을 그리는 제너릭 메서드를 작성할 수 있습니다.
public static <T extends Shape> void draw(T shape) { /* ... */ }
The Java compiler replaces T with Shape:
public static void draw(Shape shape) { /* ... */ }
다음 두 클래스가 주어집니다.
public class Node<T> {
public T data;
public Node(T data) { this.data = data; }
public void setData(T data) {
System.out.println("Node.setData");
this.data = data;
}
}
public class MyNode extends Node<Integer> {
public MyNode(Integer data) { super(data); }
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}
다음 코드를 고려하십시오.
MyNode mn = new MyNode(5);
Node n = mn; // A raw type - compiler throws an unchecked warning
n.setData("Hello"); // Causes a ClassCastException to be thrown.
Integer x = mn.data;
타입 소거 후 이 코드는 다음과 같습니다.
MyNode mn = new MyNode(5);
Node n = mn; // A raw type - compiler throws an unchecked warning
// Note: This statement could instead be the following:
// Node n = (Node)mn;
// However, the compiler doesn't generate a cast because
// it isn't required.
n.setData("Hello"); // Causes a ClassCastException to be thrown.
Integer x = (Integer)mn.data;
다음 섹션에서는 n.setData("Hello");에서 ClassCastException이 발생하는 이유를 설명합니다.
일반적으로 브리지 메서드에 대해 걱정할 필요는 없지만 스택 추적에 브리지 메서드가 나타나면 당황할 수 있습니다.
타입 소거후에, Node와 MyNode 클래스는 다음과 같이 됩니다.
public class Node {
public Object data;
public Node(Object data) { this.data = data; }
public void setData(Object data) {
System.out.println("Node.setData");
this.data = data;
}
}
public class MyNode extends Node {
public MyNode(Integer data) { super(data); }
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}
타입 소거 후 메서드 시그니처가 일치하지 않습니다. Node.setData(T) 메서드는 Node.setData(Object)가 됩니다.
결과적으로 MyNode.setData(Integer) 메서드는 Node.setData(Object) 메서드를 재정의하지 않습니다.
이 문제를 해결하고 타입 소거 후 제너릭 타입의 polymorphism을 유지하기 위해 Java 컴파일러는 하위 타입 지정이 예상대로 작동하는지 확인하는 브리지 메서드를 생성합니다.
MyNode 클래스의 경우 컴파일러는 setData에 대해 다음 브리지 메서드를 생성합니다.
class MyNode extends Node {
// Bridge method generated by the compiler
//
public void setData(Object data) {
setData((Integer) data);
}
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data); // node 부모 Data의 SetData 호출
}
// ...
}
브리지 메서드 MyNode.setData(object)는 원래 MyNode.setData(Integer) 메서드에 위임합니다.
결과적으로 n.setData("Hello"); 코드 MyNode.setData(Object) 메서드를 호출하고 "Hello"를 Integer로 캐스팅할 수 없기 때문에 ClassCastException이 발생합니다.
타입 소거(Type Erasure)라는 섹션은 컴파일러가 타입 매개변수와 타입 인자에 관련된 정보를 제거하는 과정을 설명합니다.
타입 소거는 비검증 가능한(non-reifiable) 타입을 갖는 varargs 메서드의 가변 인자(formal parameter)에 영향을 미칩니다. varargs 메서드에 대한 자세한 내용은 메서드 또는 생성자에 정보를 전달할 때 아규먼트의 임의 개수 섹션을 참조하세요.
이 페이지는 다음 주제를 다룹니다.
비실체화할 수 없는 타입 (Non-Reifiable Types)
힙 오염 (Heap Pollution)
비실체화할 수 없는 형식 매개변수를 가진 가변 인수 메서드의 잠재적인 취약점
비실체화할 수 없는 형식 매개변수를 가진 가변 인수 메서드에서 경고를 방지하는 방법
수정 불가능한 타입은 제한되지 않은(unbounded) 와일드카드로 정의되지 않은 제너릭 타입의 호출인 타입 소거에 의해 컴파일 타임에 정보가 제거된 타입입니다. 수정 불가능 타입은 런타임에 모든 정보를 사용할 수 없습니다.
수정 불가능한 타입의 예로는 List<String> 및 List<Number>가 있습니다. JVM은 런타임 시 이러한 타입 간의 차이를 구분할 수 없습니다. 제네릭에 대한 제한에서 볼 수 있듯이 수정 불가능한 타입을 사용할 수 없는 특정 상황이 있습니다.
예를 들어 instanceof 표현식에서 또는 배열의 요소로 사용할 수 있습니다.
비검증 가능한(non-reifiable) 타입이 instanceof 연산자와 배열의 요소로 사용할 수 없음을 보여주는 예시 코드.
public class Example {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
boolean isList = stringList instanceof List<String>; // 컴파일 에러!
// instanceof 연산자의 피연산자로 비검증 가능한 타입 사용 불가
}
}
위의 예시에서 stringList는 List<String> 타입의 객체입니다. 그러나 stringList instanceof List<String> 표현식은 컴파일 에러가 발생합니다.
비검증 가능한(non-reifiable) 타입인 List<String>은 instanceof 연산자의 피연산자로 사용할 수 없습니다.
public class Example {
public static void main(String[] args) {
List<String>[] stringLists = new List<String>[5]; // 컴파일 에러!
// 배열의 요소로 비검증 가능한 타입 사용 불가
}
}
위의 예시에서 stringLists는 List<String> 타입의 배열입니다. 그러나 new List<String>[5] 표현식은 컴파일 에러가 발생합니다. 비검증 가능한(non-reifiable) 타입인 List<String>은 배열의 요소로 사용할 수 없습니다.
위의 두 예시에서 비검증 가능한(non-reifiable) 타입인 List<String>을 instanceof 연산자의 피연산자로 사용하거나, 배열의 요소로 사용하려고 하면 컴파일 에러가 발생합니다. 이는 비검증 가능한(non-reifiable) 타입이 일부 제약 사항을 가지고 있어서입니다.
비검증 가능한(non-reifiable) 타입의 몇 가지 예시를 알려드리겠습니다.
1. 제네릭 타입의 타입 인자를 사용하지 않은 Raw 타입
List rawList = new ArrayList();
// Raw 타입, 비검증 가능한(non-reifiable) 타입
2. 제네릭 타입의 타입 인자로 와일드카드를 사용한 경우
List<?> wildcardList = new ArrayList<>();
// 와일드카드 사용, 비검증 가능한(non-reifiable) 타입
//List<Object> add -> element
!=
List<?> : add (x) element
3. 타입 매개변수를 사용한 제네릭 타입
public class GenericClass<T> {
// ...
}
GenericClass<String> genericClass = new GenericClass<>();
// 타입 매개변수 사용, 비검증 가능한(non-reifiable) 타입
4. 배열의 제네릭 타입
List<String>[] array = new ArrayList[5];
// 배열의 제네릭 타입, 비검증 가능한(non-reifiable) 타입
5. 제네릭 타입의 타입 인자로 상위 바운드를 사용한 경우
public class GenericClass<T extends Number> {
// ...
}
GenericClass<Integer> genericClass = new GenericClass<>();
// 타입 인자에 상위 바운드 사용, 비검증 가능한(non-reifiable) 타입
6. 제네릭 타입의 타입 인자로 하위 바운드를 사용한 경우
public class GenericClass<T super Integer> {
// ...
}
GenericClass<Number> genericClass = new GenericClass<>();
// 타입 인자에 하위 바운드 사용, 비검증 가능한(non-reifiable) 타입
7. 제네릭 메서드에서 비검증 가능한(non-reifiable) 타입을 사용한 경우
public <T> void genericMethod(List<T> list) {
// ...
}
List<?> wildcardList = new ArrayList<>();
genericMethod(wildcardList);
// 제네릭 메서드에 비검증 가능한(non-reifiable) 타입 사용
8. 와일드카드를 사용한 제네릭 타입
public class GenericClass<T> {
public void genericMethod(List<?> list) {
// ...
}
}
GenericClass<String> genericClass = new GenericClass<>();
List<Integer> integerList = new ArrayList<>();
genericClass.genericMethod(integerList);
// 와일드카드를 사용한 제네릭 타입, 비검증 가능한(non-reifiable) 타입
이로 인해 몇 가지 제약 사항과 타입 안정성과 관련된 문제가 발생할 수 있습니다. 주의해야 할 점은 비검증 가능한(non-reifiable) 타입을 다룰 때 타입 검사와 형변환에 주의를 기울여야 한다는 것입니다.
확인되지 않은 경고는 컴파일 시간(컴파일 시간 유형 검사 규칙의 제한 내) 또는 런타임에 파라미터된 타입(예: 캐스트 또는 메서드 호출)과 관련된 작업의 정확성을 확인할 수 없는 경우 생성됩니다.
예를 들어 원시 타입과 파라미터된 타입을 혼합하거나 확인되지 않은 캐스트를 수행할 때 힙 오염이 발생합니다.
정상적인 상황에서 모든 코드가 동시에 컴파일되면 컴파일러는 체크되지 않은 경고를 발행하여 잠재적인 힙 오염에 주의를 기울입니다. 코드 섹션을 별도로 컴파일하면 힙 오염의 잠재적 위험을 감지하기 어렵습니다. 코드가 경고 없이 컴파일되는지 확인하면 힙 오염이 발생하지 않습니다.
힙 오염이 발생하는 예시들.
import java.util.ArrayList;
import java.util.List;
public class HeapPollutionExample {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
List<Object> objectList = (List<Object>) stringList; // 비검증 가능한 타입의 형변환
objectList.add(10); // 잘못된 유형의 객체를 추가
String value = stringList.get(0); // ClassCastException 발생: 잘못된 유형의 객체를 가져옴
}
}
이는 힙 오염을 발생시키는 예시입니다. 마지막으로 stringList에서 첫 번째 요소를 가져올 때 ClassCastException이 발생하게 됩니다. 이는 힙 오염으로 인해 잘못된 유형의 객체가 가져와졌음을 나타냅니다.
import java.util.ArrayList;
import java.util.List;
public class HeapPollutionExample {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>(); // type 추론
List<?> unboundedList = stringList; // 와일드카드로 비검증 가능한 타입 사용
unboundedList.add(10); // 힙 오염 발생: 잘못된 유형의 객체를 추가
String value = stringList.get(0); // 컴파일 에러: 잘못된 유형의 객체를 가져오려고 함
}
}
위의 코드에서, stringList는 List<String> 타입의 리스트입니다.
그러나 unboundedList에 와일드카드(List<?>)로 할당할 때 비검증 가능한(non-reifiable) 타입이 됩니다. unboundedList에 add 메서드를 사용하여 잘못된 유형의 객체인 정수를 추가하면 힙 오염이 발생합니다.
마지막으로 stringList에서 첫 번째 요소를 가져오려고 하면 컴파일 에러가 발생합니다. 이는 힙 오염으로 인해 잘못된 유형의 객체를 가져오려고 하기 때문입니다.
이 예시에서도 힙 오염이 발생하는데, unboundedList에 비검증 가능한(non-reifiable) 타입인 와일드카드(List<?>)를 사용했기 때문입니다. 와일드카드 타입은 특정한 유형의 객체를 추가할 수 없으므로, 잘못된 유형의 객체를 추가하는 시도가 힙 오염을 발생시킵니다.
import java.util.ArrayList;
import java.util.List;
public class HeapPollutionExample {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
List<List<?>> nestedList = new ArrayList<>(); // 중첩 리스트
nestedList.add(stringList); // 비검증 가능한 타입을 포함하는 리스트 추가
List<Integer> integerList = new ArrayList<>();
nestedList.add(integerList); // 비검증 가능한 타입을 포함하는 리스트 추가
List<?> list1 = nestedList.get(0);
List<?> list2 = nestedList.get(1);
list1.add(10); // 힙 오염 발생: 잘못된 유형의 객체를 추가
list2.add("Hello"); // 힙 오염 발생: 잘못된 유형의 객체를 추가
String value = stringList.get(0); // 컴파일 에러: 잘못된 유형의 객체를 가져오려고 함
Integer number = integerList.get(0); // 컴파일 에러: 잘못된 유형의 객체를 가져오려고 함
}
}
위의 코드에서, stringList는 List<String> 타입의 리스트입니다. 이 리스트를 nestedList에 추가하고, 그 후에 integerList를 nestedList에 추가합니다.
이로 인해 nestedList는 비검증 가능한(non-reifiable) 타입인 List<?>를 포함하는 리스트가 됩니다.
그 다음, list1과 list2는 nestedList에서 가져온 리스트입니다. 이들은 와일드카드(List<?>) 타입으로 선언되었습니다.
그러나 list1.add(10)과 list2.add("Hello")에서 잘못된 유형의 객체를 추가하면 힙 오염이 발생합니다. 이는 힙 오염으로 인해 잘못된 유형의 객체가 리스트에 추가되었음을 나타냅니다.
마지막으로 stringList와 integerList에서 요소를 가져오려고 하면 컴파일 에러가 발생합니다. 이는 힙 오염으로 인해 잘못된 유형의 객체를 가져오려고 했기 때문입니다.
위의 예시에서 힙 오염은 비검증 가능한(non-reifiable) 타입인 List<?>를 다룰 때 발생합니다. 다른 유형의 리스트를 nestedList에 추가하면서 잘못된 유형의 객체가 리스트에 포함되게 되고, 이로 인해 힙 오염이 발생합니다.
힙 오염을 피하기 위해서는 제너릭 타입을 정확하게 다루고, 올바른 유형의 객체만 추가하고 가져오도록 주의해야 합니다.
Consider the following ArrayBuilder class
public class ArrayBuilder {
public static <T> void addToList (List<T> listArg, T... elements) {
for (T x : elements) {
listArg.add(x);
}
}
public static void faultyMethod(List<String>... l) {
// List<String>...l == List<String>[]
Object[] objectArray = l; // Valid
objectArray[0] = Arrays.asList(42);
String s = l[0].get(0);
// l[0].get(0); -> List<Integer>[0].get(0)
// ClassCastException thrown here
}
}
The following example, HeapPollutionExample uses the ArrayBuiler class
public class HeapPollutionExample {
public static void main(String[] args) {
List<String> stringListA = new ArrayList<String>();
List<String> stringListB = new ArrayList<String>();
ArrayBuilder.addToList(stringListA, "Seven", "Eight", "Nine");
//static method
ArrayBuilder.addToList(stringListB, "Ten", "Eleven", "Twelve");
List<List<String>> listOfStringLists =
new ArrayList<List<String>>();
ArrayBuilder.addToList(listOfStringLists,
stringListA, stringListB);
ArrayBuilder.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!"));
}
}
컴파일할 때 ArrayBuilder.addToList 메서드의 정의에 의해 다음 경고가 생성됩니다.
warning: [varargs] Possible heap pollution from parameterized vararg type T
컴파일러는 varargs 메서드를 만나면 varargs formal 파라미터를 배열로 변환합니다.
그러나 Java 프로그래밍 언어는 파라미터된 타입의 배열 작성을 허용하지 않습니다. 메서드 ArrayBuilder.addToList에서 컴파일러는 varargs formal 파라미터 T... 요소를 formal 파라미터 T[] 요소인 배열로 변환합니다.
그러나 타입 소거로 인해 컴파일러는 varargs formal 파라미터를 Object[] 요소로 변환합니다. 결과적으로 더미 오염의 가능성이 있습니다.
다음 코드문은 varargs formal 파라미터 l을 Object 배열 objectArgs에 할당합니다.
Object[] objectArray = l;
이 코드문은 잠재적으로 힙 오염을 일으킬 수 있습니다. varargs formal 파라미터 l의 파라미터화된 타입과 일치하는 값을 변수 objectArray에 할당할 수 있으므로 l 에 할당할 수 있습니다.
그러나 컴파일러는 이 코드문에서 확인되지 않은 경고를 생성하지 않습니다.
컴파일러는 varargs formal 파라미터 List<String>... l을 formal 파라미터 List[] l로 변환할 때 이미 경고를 생성했습니다.
이 코드문은 유효합니다. 변수 l은 Object[]의 subtype인 List[] 타입을 가집니다.
결과적으로 컴파일러는 다음 코드문에 표시된 대로 objectArray 배열의 배열 구성요소에 모든 타입의 List 객체를 할당하는 경우 경고 또는 오류를 발행하지 않습니다.
objectArray[0] = Arrays.asList(42);
이 코드문은 Integer 타입의 객체 하나를 포함하는 List 객체와 함께 objectArray 배열의 첫 번째 배열 구성요소에 할당합니다.
다음 코드문으로 ArrayBuilder.faultyMethod를 호출한다고 가정합니다.
ArrayBuilder.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!"));
런타임 시 JVM은 다음 코드문에서 ClassCastException을 발생시킵니다.
// ClassCastException thrown here String s = l[0].get(0);
변수 l의 첫 번째 배열 구성 요소에 저장된 객체의 타입은 List<Integer>이지만 이 코드문은 List<String> 타입의 객체를 예상합니다.
파라미터화된 타입의 파라미터가 있는 varargs 메서드를 선언하고 메서드 본문이 varargs formal 파라미터의 부적절한 처리로 인해 ClassCastException 또는 기타 유사한 예외를 throw하지 않는지 확인하면 컴파일러가 다음과 같은 경고를 방지할 수 있습니다. 정적 및 비 생성자 메서드 선언에 다음 주석을 추가하여 이러한 종류의 varargs 메서드를 생성합니다.
@SafeVarargs
@SafeVarargs 주석은 메서드 contract의 문서화된 부분입니다. 이 주석은 메소드의 구현이 varargs formal 파라미터를 부적절하게 처리하지 않을 것이라고 주장합니다.
바람직하지는 않지만 메소드 선언에 다음을 추가하여 이러한 경고를 억제하는 것도 가능합니다.
@SuppressWarnings({"unchecked", "varargs"})
그러나 이 방법은 메서드 호출 사이트에서 생성된 경고를 억제하지 않습니다. @SuppressWarnings 구문에 익숙하지 않은 경우 주석을 참조하십시오.
Cannot Instantiate Generic Types with Primitive Types
Cannot Create Instances of Type Parameters
Cannot Declare Static Fields Whose Types are Type Parameters
Cannot Use Casts or instanceof With Parameterized Types
Cannot Create Arrays of Parameterized Types
Cannot Create, Catch, or Throw Objects of Parameterized Types
Cannot Overload a Method Where the Formal Parameter Types of Each Overload Erase to the Same Raw Type
Restrictions on Generics (The Java™ Tutorials > Learning the Java Language > Generics (Updated))
The Java Tutorials have been written for JDK 8. Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available. See Java Language Changes for a summary of updated
docs.oracle.com
Restrictions on Generics (The Java™ Tutorials > Learning the Java Language > Generics (Updated))
The Java Tutorials have been written for JDK 8. Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available. See Java Language Changes for a summary of updated
docs.oracle.com
Restrictions on Generics (The Java™ Tutorials > Learning the Java Language > Generics (Updated))
The Java Tutorials have been written for JDK 8. Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available. See Java Language Changes for a summary of updated
docs.oracle.com
Cannot Instantiate Generic Types with Primitive Types
Consider the following parameterized type
class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
// ...
}
When creating a Pair object, you cannot substitute a primitive type for the type parameter K or V:
Pair<int, char> p = new Pair<>(8, 'a'); // compile-time error
You can substitute only non-primitive types for the type parameters K and V:
Pair<Integer, Character> p = new Pair<>(8, 'a');
Note that the Java compiler autoboxes 8 to Integer.valueOf(8) and 'a' to Character('a'):
Pair<Integer, Character> p = new Pair<>(Integer.valueOf(8), new Character('a'));
For more information on autoboxing, see Autoboxing and Unboxing in the Numbers and Strings lesson.
Cannot Create Instances of Type Parameters
You cannot create an instance of a type parameter. For example, the following code causes a compile-time error:
public static <E> void append(List<E> list) {
E elem = new E(); // compile-time error
list.add(elem);
}
As a workaround, you can create an object of a type parameter through reflection:
public static <E> void append(List<E> list, Class<E> cls) throws Exception {
E elem = cls.newInstance(); // OK
list.add(elem);
}
You can invoke the append method as follows:
List<String> ls = new ArrayList<>();
append(ls, String.class);
Cannot Declare Static Fields Whose Types are Type Parameters
A class's static field is a class-level variable shared by all non-static objects of the class. Hence, static fields of type parameters are not allowed. Consider the following class:
public class MobileDevice<T> {
private static T os;
// ...
}
If static fields of type parameters were allowed, then the following code would be confused:
MobileDevice<Smartphone> phone = new MobileDevice<>();
MobileDevice<Pager> pager = new MobileDevice<>();
MobileDevice<TabletPC> pc = new MobileDevice<>();
Because the static field os is shared by phone, pager, and pc, what is the actual type of os? It cannot be Smartphone, Pager, and TabletPC at the same time. You cannot, therefore, create static fields of type parameters.
Cannot Use Casts or instanceof with Parameterized Types
Because the Java compiler erases all type parameters in generic code, you cannot verify which parameterized type for a generic type is being used at runtime:
public static <E> void rtti(List<E> list) {
if (list instanceof ArrayList<Integer>) { // compile-time error
// ...
}
}
The set of parameterized types passed to the rtti method is:
S = { ArrayList<Integer>, ArrayList<String> LinkedList<Character>, ... }
The runtime does not keep track of type parameters, so it cannot tell the difference between an ArrayList<Integer> and an ArrayList<String>. The most you can do is to use an unbounded wildcard to verify that the list is an ArrayList:
public static void rtti(List<?> list) {
if (list instanceof ArrayList<?>) { // OK; instanceof requires a reifiable type
// ...
}
}
Typically, you cannot cast to a parameterized type unless it is parameterized by unbounded wildcards. For example:
List<Integer> li = new ArrayList<>();
List<Number> ln = (List<Number>) li; // compile-time error
However, in some cases the compiler knows that a type parameter is always valid and allows the cast. For example:
List<String> l1 = ...;
ArrayList<String> l2 = (ArrayList<String>)l1; // OK
Cannot Create Arrays of Parameterized Types
You cannot create arrays of parameterized types. For example, the following code does not compile:
List<Integer>[] arrayOfLists = new List<Integer>[2]; // compile-time error
The following code illustrates what happens when different types are inserted into an array:
Object[] strings = new String[2];
strings[0] = "hi"; // OK
strings[1] = 100; // An ArrayStoreException is thrown.
If you try the same thing with a generic list, there would be a problem:
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.
If arrays of parameterized lists were allowed, the previous code would fail to throw the desired ArrayStoreException.
Cannot Create, Catch, or Throw Objects of Parameterized Types
A generic class cannot extend the Throwable class directly or indirectly. For example, the following classes will not compile:
// Extends Throwable indirectly
class MathException<T> extends Exception { /* ... */ } // compile-time error
// Extends Throwable directly
class QueueFullException<T> extends Throwable { /* ... */ // compile-time error
A method cannot catch an instance of a type parameter:
public static <T extends Exception, J> void execute(List<J> jobs) {
try {
for (J job : jobs)
// ...
} catch (T e) { // compile-time error
// ...
}
}
You can, however, use a type parameter in a throws clause:
class Parser<T extends Exception> {
public void parse(File file) throws T { // OK
// ...
}
}
Cannot Overload a Method Where the Formal Parameter Types of Each Overload Erase to the Same Raw Type
A class cannot have two overloaded methods that will have the same signature after type erasure.
public class Example {
public void print(Set<String> strSet) { }
public void print(Set<Integer> intSet) { }
}
The overloads would all share the same classfile representation and will generate a compile-time error.