- 23. Don't use raw types in new code
- 24. Eliminate unchecked warnings
- 25. Prefer
- 26. Favor generic types
- 27. Favor generic method
- 28. Use bounded wildcards to increase API flexibility
- 29. Consider typesafe heterogeneous containers
23. Don't use raw types in new code
Raw type generics is the type without the parameterized type. For example,
List is the raw type of
Replace the first line with:
Using raw type will lose the security advantage of generics. Then why Java allow raw types? For historical reasons.
List, code likes this is avoiding type checking.
List<String> lst2, then
lst = lst2 is valid, but
lst1 = lst2 is invalid.
List<String> is a subclass of
List, but is not a subclass of
Program will receive a warning at
list.add(o). I know you want to allow the element to be any type. You can use
List<?> instead of
?, the unbounded wildcard type, is a safe type.
null cannot be put into
Two exceptions: When to use raw type in new code
Generics information will be erase at runtime. It only takes effect at compile time.
- In class literal(like
List.class), we must use raw type.
instanceof List<String>is invalid.
Code below is a way to use
instanceof when using generics
Bounded type parameter:
<E extends Number>
Recursive type bound:
<T extends Comparable<T>>
Bounded wildcard type:
List<? extends Number>
static <E> List<E> asList(E a)
24. Eliminate unchecked warnings
Eliminate every unchecked warnings that you can.
If the warning cannot be eliminated, and you can prove the code is safe, then you can use a
@SuppressWarnings("unchecked") annotation to eliminate the warning.
@SuppressWarnings("unchecked") annotation, you must comment why it is correct and safe.
Array is covariant, which means if
Sub is a subclass of
Sub is a subclass of
List is invariant.
This actually means, array has fault, not generics.
Another difference between array and generics is, array does type checking during runtime, while generics does that during compilation. Array throws exception if error occurs.
Generics does type checking while compilation, and then use erasure to erase the parameterized type information in runtime. After erasure, generics can be used with unbounded generics.
Creating an array of a generic type, a parameterized type or a type parameter are illegal, because the type of array is not typesafe
This code is illegal
If 1st line is correct, then 3rd line, it is correct, because at runtime,
stringList is just
List after erasure. Then 4th line, it is equivalent to the first element of
List is a
List(after erasure, but actually it is
List<Integer>. 4th line is legal. But 5th line is illegal, we cannot get s, since it is a
List<String> is non-reifiable type, which is a type that at runtime the information in the memory representation is little than compilation time.
List<?> is legal.
Solution is prefer
List<E> instead of
E. Less performance and simplexity, more secure and flexibility.
It has a warning
[unchecked] unchecked cast.
toArray() returns a Object, casting to
E is dangerous.
26. Favor generic types
We are programming a Stack class,
public class Stack<E>.
You may want to store the element in an array like
private E elements inside the class. In the constructor, when you initialize
elements, you will get a warning or error on
elements = new E[DEFAULT_INITIAL_CAPACITY]. Because you cannot create a non-reifiable type array.
There are two ways to solve this.
elements = (E) new Object[DEFAULT_INITIAL_CAPACITY]. You will get another warning says
unchecked cast. The compiler cannot guarantee the casting is correct. You can use a
@SuppressWarnings("unchecked") to avoid the warning.
Change the type of
Object elements. Then you may get error when you do stack operations like on
E result = elements[--size] you will get
incompatible types. If you change it to
E result = (E) elements[--size], you will get a warning
unchecked cast. Since
E is a non-reifiable type, the compiler cannot to type checking when casting. Still you can use
Both solutions are ok. The difference is that first solution will annotate a whole array as
@SuppressWarnings, which is more dangerous than the second solution. Thus the second one is better. But the second solution may require a lot of type casting to
E,. That's why the first solution is more common in use.
Final code(using solution 1):
27. Favor generic method
Example of generic method
You can use bounded wildcard type to make it more flexible.
Generic has a mechanism called type inference. It will know what is the generic type you want to use.
There is a redundant in declaration of a generic object.
Map<String, List<String>> anagrams = new HashMap<String, List<String>>(), the left side and right side both need the full type parameters. We can have a generic static factory method to do it.
Example: generic singleton factory
If the object we generate from the generic static factory is a singleton, since generics use erasure mechanism, we may need to create many different types of singletons.
Suppose we have an interface
Now we need an identity function, implement this
UnaryFunction, we may need create a new one every time we use it for a new type.
The whole program:
This one will have an warning, since not all the
IDENTITY_FUNCTION can be casted to
UnaryFunction<T>. But we know it is safe here.
Example: recursive type bound
How to express the constraint that in a list, every element is able to compare with another element in the list. For example, to get the maximum element from this type of list:
By doing so we ensure that type
T is able to compare with the same type object.
28. Use bounded wildcards to increase API flexibility
We have a piece of code:
You cannot push
numberStack because the types are not match.
After using bounded wildcard:
If you also want a
Pay attention to
Collection< ? super E>.
For maximum flexibility, use wildcard types on input parameters that represent producers or consumers.
PECS: producer-extends, consumer-super. producer provides things, will not change itself. consumer use things, will change itself. So in previous example,, dst use the object from stack, it is a consumer.
Apply PECS to the stack example in section 27:
Notice that the return type of
Unfortunately, You will get an error:
incompatible types. Because compiler cannot inference your type
The Solution is explicit type parameter. If the compiler cannot infer the correct type, we can try this.
More examples: recursive type bound
Let's look back at the
max() int section 27. Apply PECS to that. Previous method is:
But it has a compilation error. It says
list is not a
List<T>. We need to replace the iterator with
Iterator< ? extends T > i= list.iterator().
29. Consider typesafe heterogeneous containers
In Java 1.5,
Class type becomes
String.class returns a value of type
We have a
Favorites class, to store one single favorite object for each type of things.
We will get
Map< Class< ? >, Object> can have any type of key.The value is
Object, which means it does not guarantee the correct relationship between key and value.
To solve the problem, we can change
favorites.put(type, instance) to
Another problem of
Favorite is, we cannot call something like
pushFavorite(List<String>.class, lst), because
List<String> is non-reifiable. Currently it does not have a satisfying solution.