What are wildcards? Listing 21.6 gives an example to demonstrate the needs they address. The example declares a generic max method for finding the maximum in a stack of numbers (lines 12 “22). The main method creates a stack of integer objects, adds three integers to the stack, and invokes the max method to find the maximum number in the stack.
1 public class WildCardDemo1 { 2 public static void main(String[] args) { 3 GenericStack<Integer> intStack = new GenericStack<Integer>(); 4 intStack.push( 1 ); // 1 is autoboxed into new Integer(1) 5 intStack.push( 2 ); 6 intStack.push( - 2 ); 7 8 System.out.print( "The max number is " + max(intStack)); 9 } 10 11 // Find the maximum in a stack of numbers 12 public static double max( GenericStack<Number> stack) { 13 double max = stack.pop().doubleValue(); // initialize max 14 15 while (!stack.isEmpty()) { 16 double value = stack.pop().doubleValue(); 17 if (value > max) 18 max = value; 19 } 20 21 return max; 22 } 23 } |
The program in Listing 21.6 has a compilation error in line 8 because intStack is not an instance of GenericStack<Number> . So you cannot invoke max(intStack) .
The fact is that Integer is a subtype of Number , but GenericStack<Integer> is not a subtype of GenericStack<Number> . To circumvent this problem, JDK 1.5 introduces wildcards. A wildcard represents a type in the form of ? , ? extends T , or ? super T , where T is a type.
The first form, ? , called an unbounded wildcard , is the same as ? extends Object . The second form, ? extends T , called a bounded wildcard , represents T or an unknown subtype of T . The third form, ? super T , called a lower bound wildcard , denotes T or an unknown supertype of T .
You can fix the error by replacing line 11 in Listing 21.6 as follows :
public static double max( GenericStack<? extends Number> stack) {
<? extends Number> is a wildcard type that represents Number or a subtype of Number . So it is legal to invoke max(new GenericStack<Integer>()) or max(new GenericStack<Double>()) .
Listing 21.7 shows an example of using the ? wildcard in the print method that prints objects in a stack and empties the stack. <?> is a wildcard that represents any object type. It is equivalent to <? Extends Object> . What happens if you replace GenericStack<?> by GenericStack<Object> ? It would be wrong to invoke print(intStack) , because instack is not an instance of GenericStack<Object> . Please note that GenericStack<Integer> is not a subtype of GenericStack<Object> , although Integer is a subtype of Object .
1 public class WildCardDemo2 { 2 public static void main(String[] args) { 3 GenericStack<Integer> intStack = new GenericStack<Integer>(); 4 intStack.push( 1 ); // 1 is autoboxed into new Integer(1) 5 intStack.push( 2 ); 6 intStack.push( -2 ); 7 8 print(intStack); 9 } 10 11 /** Print objects and empties the stack */ 12 public static void print( GenericStack<?> stack) { 13 while (!stack.isEmpty()) { 14 System.out.print(stack.pop() + " " ); 15 } 16 } 17 } |
When is the wildcard <? super T> needed? Consider the example in Listing 21.8. The example creates a stack of strings in stack1 (line 3) and a stack of objects in stack2 (line 4) and invokes add(stack1, stack2) (line 9) to add the strings in stack1 into stack2 . GenericStack<? super T> is used to declare stack2 in line 12. If <? super T> is replaced by <T> , a compilation error would occur on add(stack1, stack2) in line 9, because stack1 's type is GenericStack<String> and stack2 's type is GenericStack<Object> . <? super T> represents type T or a supertype of T . GenericStack<Object> is a subtype of GenericStack<? super String> .
1 public class WildCardDemo3 { 2 public static void main(String[] args) { 3 GenericStack<String> stack1 = new GenericStack<String>(); 4 GenericStack<Object> stack2 = new GenericStack<Object>(); 5 stack2.push( "Java" ); 6 stack2.push( 2 ); 7 stack1.push( "Sun" ); 8 add(stack1, stack2); 9 WildCardDemo2.print(stack2); 10 } 11 12 public static <T> void add(GenericStack <T> stack1, 13 GenericStack <? super T> stack2) { 14 while (!stack1.isEmpty()) 15 stack2.push(stack1.pop()); 16 } 17 } |
The relationship among generic types and wildcard types is summarized in Figure 21.6. In this figure, A and B represent classes or interfaces, and E is a generic type parameter.