To use Java generics effectively, you must consider the following restrictions:
- 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
- 30 Answers 30
Cannot Instantiate Generic Types with Primitive Types
Consider the following parameterized type:
When creating a Pair object, you cannot substitute a primitive type for the type parameter K or V:
You can substitute only non-primitive types for the type parameters K and V:
Note that the Java compiler autoboxes 8 to Integer.valueOf(8) and ‘a‘ to 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:
As a workaround, you can create an object of a type parameter through reflection:
You can invoke the append method as follows:
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:
If static fields of type parameters were allowed, then the following code would be confused:
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:
The set of parameterized types passed to the rtti method is:
The runtime does not keep track of type parameters, so it cannot tell the difference between an ArrayList and an ArrayList . The most you can do is to use an unbounded wildcard to verify that the list is an ArrayList:
Typically, you cannot cast to a parameterized type unless it is parameterized by unbounded wildcards. For example:
However, in some cases the compiler knows that a type parameter is always valid and allows the cast. For example:
Cannot Create Arrays of Parameterized Types
You cannot create arrays of parameterized types. For example, the following code does not compile:
The following code illustrates what happens when different types are inserted into an array:
If you try the same thing with a generic list, there would be a problem:
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:
A method cannot catch an instance of a type parameter:
You can, however, use a type parameter in a throws clause:
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.
The overloads would all share the same classfile representation and will generate a compile-time error.
Due to the implementation of Java generics, you can’t have code like this:
How can I implement this while maintaining type safety?
I saw a solution on the Java forums that goes like this:
But I really don’t get what’s going on.
30 Answers 30
I have to ask a question in return: is your GenSet «checked» or «unchecked»? What does that mean?
Checked: strong typing. GenSet knows explicitly what type of objects it contains (i.e. its constructor was explicitly called with a Class argument, and methods will throw an exception when they are passed arguments that are not of type E . See Collections.checkedCollection .
-> in that case, you should write:
Unchecked: weak typing. No type checking is actually done on any of the objects passed as argument.
-> in that case, you should write
Note that the component type of the array should be the erasure of the type parameter:
All of this results from a known, and deliberate, weakness of generics in Java: it was implemented using erasure, so «generic» classes don’t know what type argument they were created with at run time, and therefore can not provide type-safety unless some explicit mechanism (type-checking) is implemented.
You can do this:
This is one of the suggested ways of implementing a generic collection in Effective Java; Item 26. No type errors, no need to cast the array repeatedly. However this triggers a warning because it is potentially dangerous, and should be used with caution. As detailed in the comments, this Object is now masquerading as our E type, and can cause unexpected errors or ClassCastException s if used unsafely.
As a rule of thumb, this behavior is safe as long as the cast array is used internally (e.g. to back a data structure), and not returned or exposed to client code. Should you need to return an array of a generic type to other code, the reflection Array class you mention is the right way to go.
Worth mentioning that wherever possible, you’ll have a much happier time working with List s rather than arrays if you’re using generics. Certainly sometimes you don’t have a choice, but using the collections framework is far more robust.
Here’s how to use generics to get an array of precisely the type you’re looking for while preserving type safety (as opposed to the other answers, which will either give you back an Object array or result in warnings at compile time):
That compiles without warnings, and as you can see in main , for whatever type you declare an instance of GenSet as, you can assign a to an array of that type, and you can assign an element from a to a variable of that type, meaning that the array and the values in the array are of the correct type.
It works by using class literals as runtime type tokens, as discussed in the Java Tutorials. Class literals are treated by the compiler as instances of java.lang.Class . To use one, simply follow the name of a class with .class . So, String.class acts as a Class object representing the class String . This also works for interfaces, enums, any-dimensional arrays (e.g. String.class ), primitives (e.g. int.class ), and the keyword void (i.e. void.class ).
Class itself is generic (declared as Class , where T stands for the type that the Class object is representing), meaning that the type of String.class is Class .
So, whenever you call the constructor for GenSet , you pass in a class literal for the first argument representing an array of the GenSet instance’s declared type (e.g. String.class for GenSet ). Note that you won’t be able to get an array of primitives, since primitives can’t be used for type variables.
Inside the constructor, calling the method cast returns the passed Object argument cast to the class represented by the Class object on which the method was called. Calling the static method newInstance in java.lang.reflect.Array returns as an Object an array of the type represented by the Class object passed as the first argument and of the length specified by the int passed as the second argument. Calling the method getComponentType returns a Class object representing the component type of the array represented by the Class object on which the method was called (e.g. String.class for String.class , null if the Class object doesn’t represent an array).
That last sentence isn’t entirely accurate. Calling String.class.getComponentType() returns a Class object representing the class String , but its type is Class , not Class , which is why you can’t do something like the following.
Same goes for every method in Class that returns a Class object.
Regarding Joachim Sauer’s comment on this answer (I don’t have enough reputation to comment on it myself), the example using the cast to T will result in a warning because the compiler can’t guarantee type safety in that case.
To understand this topic let us directly start with an example.
You will find that a simple statement like this will not even compile because the Java compiler does not allow this. To understand the reason, you first need to know two arrays are covariant and generics are invariant.
Covariant: It means you can assign subclass type array to its superclass array reference. For instance,
Invariant: It means you cannot assign subclass type generic to its super class generic reference because in generics any two distinct types are neither a subtype nor a supertype. For instance,
Because of this fundamental reason, arrays and generics do not fit well with each other.
Now let’s get back to the actual question. If generic array creation were legal, then compiler generated casts would correct the program at compile time but it can fail at runtime, which violates the core fundamental system of generic types.
Let us consider the following example to understand that:-
As we assumed generic array creation is legal, so line 1 is valid and creates an array of ID List.
In line 2, we have created a simple list of string.
In line 3, we passed an arrayOfIdList object to objArray reference, which is legal because arrays are covariant.
In line 4, we have assigned nameList (i.e. the list of string) into objArray that is pointing to the arrayOfIdList object. It is alright because of Type Erasure, means the runtime instance of List is List and List arrayOfIdList is list, so this will not generate any exception. Here comes the biggest problem, we have assigned the list of string (i.e., List ) into an array that can only contain the list of integer.
In line 5, mistakenly, we are trying to get the first element of the 0th element of an array. As arrayOfIdList declared as an array of integer list, the compiler will cast that assignment to Integer which will generate ClassCastException at runtime.