Wildcards in Java Generics

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
rawList = new ArrayList();
rawList = new ArrayList<String>();
rawList = new ArrayList(); //ok
//complication error
rawList = new ArrayList<String>();
rawList = new ArrayList(); //ok
rawList = new ArrayList<String>();//ok
list.add() Anything, all lines below are ok

rawList.add("testStr");
rawList.add(1);
rawList.add(Integer.class);
rawList.add(null);

 

Anything, all lines below are ok

objList.add("testStr");
objList.add(1);
objList.add(Integer.class);
objList.add(null);
NULL only

//complication error
objList.add("testStr");
//complication error
objList.add(1);
//complication error
objList.add(Integer.class);
objList.add(null);//ok
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
extendsBList = new ArrayList<A>();//compilation error
extendsBList = new ArrayList<B>();
extendsBList = new ArrayList<C>();
superBList = new ArrayList<A>();
superBList = new ArrayList<B>();
superBList = new ArrayList<C>();//compilation error
superBList = new ArrayList<Object>();
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.

//compilation for all below
extendsBList.add(new Object());
extendsBList.add(new A());
extendsBList.add(new B());
extendsBList.add(new C());
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.

superBList.add(new Object());//compilation error
superBList.add(new A());//compilation error
superBList.add(new B());//ok
superBList.add(new C());//ok
list.get() B

//ok
for(B b:extendsBList){
/do something
}
//compilation error
for(C c:extendsBList){
//do something
}
Object

//ok
for(Object obj:superBList){
    //do something
}
//compilation error
for(B b:superBList){
    //do something
}

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