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

Maven: Add local dependencies

From time to time, we need to work with legacy codes.
Recently I got a chance to look at some old modules. They were developed many years ago, Ant is used as build tool. Each of these modules has its lib folder, which has all the dependent jars. And some of the Ant build scripts have its own “typedef” task and the the typedef task class comes from the module itself. They are left behind comparing to other stuff I work on in term of SDLC stack. I decided to move those modules to Maven first.
To keep it simple, I decided to keep the jars as they are, give it a try with “system” scope. It turns out it works perfectly.

Add local jars as Maven dependencies

<dependency>
    <groupId>io.wwei.sample</groupId>
    <artifactId>stmp</artifactId>
    <version>1.0.0</version>
    <scope>system</scope>
    <systemPath>${basedir}/lib/stmp.jar</systemPath>
</dependency>

Execute Java Classes as part of “package” step

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>1.4.0</version>
    <executions>
        <execution>
            <id>exec1</id>
            <phase>package</phase>
            <goals>
                <goal>java</goal>
            </goals>
            <configuration>
                <mainClass>io.wwei.sample.PostCompile</mainClass>
                <additionalClasspathElements>
                    <additionalClasspathElement>{basedir}/target/classes</additionalClasspathElement>
                </additionalClasspathElements>
                <classpathScope>system</classpathScope>
                <commandlineArgs>local</commandlineArgs>
            </configuration>
        </execution>
    </executions>
</plugin>

Include local jars to final assembly

“dependencySets” does not include local jars, so we have to copy them explicitily as below

<fileSets>
    <fileSet>
        <directory>lib</directory>
        <outputDirectory>lib</outputDirectory>
        <includes>
            <include>**/*</include>
        </includes>
    </fileSet>
</fileSets>