Lower Bounds
Wildcard instantiations actually allow another type of bound called a lower bound as well. A lower bound is specified with the keyword super and requires that instantiations be of a certain type or any of its supertypes, up to upper bound of Type variable or Object. Consider the following classes and statement :
The wildcard instantiation Gen<? super Data3> obj123 creates a type that can hold any instantiation of Gen on the type Data3 or any of its supertypes. That means the wildcard type can be assigned one of only four types: Gen<Data3> , Gen<Data2> , Gen<Data1> or Gen<Object>. We have cut off the object inheritance hierarchy after four generations. No further subclasses of Data3 can be used.
We know that the elements of any instantiation matching our lower bounded wildcard must be a supertype of the lower bound. So, we can write to the object through our lower bound wildcard instantiation obj123. That object must be a type Data3 or its subclass. For example :
We cannot write supertypes of Data3, because the compiler do not know what supertype of Data3 the elements are. For example :
We cannot read the object as any specific type from the lower bound wildcard instantiation, we can read the object type as upper bound of Type variable. Because the compiler do not know what supertype of the specified the elements are. So, we can always read the type as Object through our wildcard instantiation. The type Object is the default upper bound. For example:
One last thing about lower bounds: only the wildcard instantiation syntax can use the super keyword to refer to lower bounds. Bounds of type variables in generic class declarations cannot have lower bounds:
Program
class Data1 { int da1; Data1(int d1) { da1 = d1; } } class Data2 extends Data1 { int da2; Data2(int d1, int d2) { super(d1); da2 = d2; } } class Data3 extends Data2 { int da3; Data3(int d1, int d2, int d3) { super(d1, d2); da3 = d3; } } class Data4 extends Data3 { int da4; Data4(int d1, int d2, int d3, int d4) { super(d1, d2, d3); da4 = d4; } } class Data5 extends Data4 { int da5; Data5(int d1, int d2, int d3, int d4, int d5) { super(d1, d2, d3, d4); da5 = d5; } } class Gen<T> { T tarray[]; Gen(T tar[]) { tarray = tar; } void setT(int pos, T t) { tarray[pos] = t; } T getT(int pos) { return tarray[pos]; } } public class Javaapp { public static void main(String[] args) { Gen<? super Data3> data123 = new Gen<Data2>(new Data2[5]); data123.setT(0, new Data3(10, 20, 30)); data123.setT(1, new Data4(10, 20, 30, 40)); data123.setT(2, new Data5(10, 20, 30, 40, 50)); } }