Generics is one of my favorite feature in Java – strong type check, no need to type cast, parameterized type in class/method implementation. Despite using it for several years, I realized that I still don’t fully understand wildcards.
Wildcards in Generics
Refer to Oracle Tutorial on wildcards
In generic code, the question mark (?), called the wildcard, represents an unknown type. The wildcard can be used in a variety of situations: as the type of a parameter, field, or local variable; sometimes as a return type (though it is better programming practice to be more specific). The wildcard is never used as a type argument for a generic method invocation, a generic class instance creation, or a supertype.
By above definition, List<?> wildList should be read as List of unknown types. Because the type is unknown to compiler, the compiler will not allow adding anything to the list other than null (instead of accepting everything !)
List vs List<Object> vs List<?>
Declared type | List rawList = … | List<Object> objList = … | List<?> unknownList = … |
---|---|---|---|
Checked? | No, raw type | Yes, checked type <Object> | Yes, checked but type is unknown |
Assignment |
|
|
|
list.add() | Anything, all lines below are ok
|
Anything, all lines below are ok
|
NULL only
|
list.get() | Object | Object | Object |
Upper and Lower Bounded Wildcards
Sample Classes
private static class A{
}
private static class B extends A{
}
private static class C extends B{
}
Upper Bounded Type | Lower Bounded Type | |
---|---|---|
Expression | List<? extends B> extendBList=… | List<? super B> superBList=… |
Read as | A List of unknown type, the type could be B or subclass of B. in this example, a List<B> or List<C> | A List of unknown type, the type could be B or superclass of B. in this example, a List<A> or List<B> or List<Object> |
Assignment |
|
|
list.add() | Because the complier does not know the list is a List<B> or List<C>, to be safe, it only allow adding instances of class C, which is also a B instance.
|
Because the complier does not know the list is a List<A> or List<B> or List<Object>, to be safe, it only allow adding instances of class A.
|
list.get() | B
|
Object
|
which means:
List<? extends B> is read-only, and only as type B
List<? super B> is kind of write-only as whatever is reading from the list, can get a Object only and does not know the actual type
So, we can only copy data from a List<? extends B> to List<? super B>, but not the other way around
This is pretty much where PECS (Producer Extends, Consumer Super) comes from.
References:
Guidelines for Wildcard Use
Stackoverflow: Difference between <? super T> and <? extends T> in Java
年更了又