Bounded Wildcards
A bounded wildcard is a wildcard that uses the extends keyword just as a type variable would to limit the range of assignable types. For example :
Our ‘abc’ variable is limited to holding instantiations of Gen on parameter types of A and its subclasses(B and C). So, we can assign it a Gen<A> or a Gen<B> or a Gen<C>. The unbounded wildcard instantiation is a kind of supertype of all of these concrete instantiations :
In the same way that the unbounded wildcard serves as a superclass for all instantiations of a generic type, bounded wildcards create more limited supertypes covering a narrower range of instantiations. In this case, our wildcard instantiation, Gen<? extends A>, is the supertype of all instantiations of Gen on A types. As with type parameter bounds, the bound A is called the upper bound of the type.
Wildcard arguments can be bounded in much the same way that a type parameter can be bounded. A bounded wildcard is especially important when you are creating a generic type that will operate on a class hierarchy. To understand why, let’s work through an example. Consider the hierarchy of classes : A,B,C. At the top of the hierarchy is A, which encapsulates a data ‘a’ . A is inherited by B, which creating an ‘ab’ data. B is inherited by C, which creating an ‘abc’ data. Shown next is a generic class called Gen, which stores an array of A :
Notice that Gen specifies a type parameter bounded by A. This means that any array stored in a Gen object will contain objects of type A or one of its subclasses. Now, assume that you want to write a method that displays the value of ‘a’ for each element in the ‘t’ array of a Gen object. Because all types of Gen objects have at least one value (‘a’), this is easy to do using a wildcard :
Because Gen is a bounded generic type that specifies A as an upper bound, all objects that can be used to create a Gen object will be arrays of type A, or of classes derived from A(B,C). Thus, show_All_a( ) can display the contents of any Gen object. However, what if you want to create a method that displays the ‘a’ and ‘b’ values of a B or C object? The trouble is that not all Gen objects will have two values, because a Gen<A> object will only have ‘a’. Therefore, how do you write a method that displays the ‘a’ and ‘b’ values for Gen<B> and Gen<C> objects, while preventing that method from being used with Gen<A> objects? The answer is the bounded wildcard argument.
A bounded wildcard enables you to restrict the types of objects upon which a method will operate. The most common bounded wildcard is the upper bound, which is created using an extends clause in much the same way it is used to create a bounded type. Using a bounded wildcard, it is easy to create a method that displays the ‘a’ and ‘b’ values of a Gen object, if that object actually has those two values. For example, the following show_BC_ab( ) method shows the ‘a’ and ‘b’ values of the elements stored in a Gen object, if those elements are actually of type B or its subclasses of B(C) :
The extends clause has been added to the wildcard in the declaration of parameter ‘ge’. It states that the ? can match any type as long as it is B, or a class derived from B. Thus, the extends clause establishes an upper bound that the ? can match. Because of this bound, show_BC_ab( ) can be called with references to objects of type Gen<B> or Gen<C>, but not with a reference of type Gen<A>. Attempting to call show_BC_ab( ) with a Gen<A> reference results in a compile-time error, thus ensuring type safety.
Program
class A { int a; A(int aa) { a = aa; } } class B extends A { int b; B(int aa, int bb) { super(aa); b = bb; } } class C extends B { int c; C(int aa, int bb, int cc) { super(aa, bb); c = cc; } } class Gen<T extends A> { T t[]; Gen(T tt[]) { t = tt; } } public class Javaapp { static void show_All_a(Gen<?> ge) { System.out.println(".........show_All_a........."); for (int i = 0; i < ge.t.length; i++) { System.out.println("a-> : " + ge.t[i].a); } } static void show_BC_ab(Gen<? extends B> ge) { System.out.println(".........show_BC_ab........."); for (int i = 0; i < ge.t.length; i++) { System.out.println("a-> : " + ge.t[i].a + " b-> : " + ge.t[i].b); } } static void show_C_abc(Gen<? extends C> ge) { System.out.println(".........show_C_abc........."); for (int i = 0; i < ge.t.length; i++) { System.out.println("a-> : " + ge.t[i].a + " b-> : " + ge.t[i].b + " c-> : " + ge.t[i].c); } } public static void main(String[] args) { Gen<A> a = new Gen<A>(new A[]{new A(30), new A(60), new A(90)}); Gen<B> b = new Gen<B>(new B[]{new B(15, 30), new B(45, 60), new B(75, 90)}); Gen<C> c = new Gen<C>(new C[]{new C(10, 20, 30), new C(40, 50, 60), new C(70, 80, 90)}); show_All_a(a); show_All_a(b); show_All_a(c); show_BC_ab(b); show_BC_ab(c); show_C_abc(c); } }