Raw Types
The test programs for generic class Stack in Section 18.6 instantiate Stacks with type arguments Double and Integer. It is also possible to instantiate generic class Stack without specifying a type argument, as follows:
Stack objectStack = new Stack( 5 ); // no type argument specified
In this case, the objectStack is said to have a raw type, which means that the compiler implicitly uses type Object throughout the generic class for each type argument. Thus the preceding statement creates a Stack that can store objects of any type. This is important for backwards compatibility with prior versions of Java. For example, the data structures of the Java Collections Framework (see Chapter 19, Collections) all stored references to Objects, but are now implemented as generic types.
A raw type Stack variable can be assigned a Stack that specifies a type argument, such as a Stack< Double > object, as follows:
Stack rawTypeStack2 = new Stack< Double >( 5 );
because type Double is a subclass of Object. This assignment is allowed because the elements in a Stack< Double > (i.e., Double objects) are certainly objectsclass Double is an indirect subclass of Object.
Similarly, a Stack variable that specifies a type argument in its declaration can be assigned a raw type Stack object, as in:
Stack< Integer > integerStack = new Stack( 10 );
Although this assignment is permitted, it is unsafe because a Stack of raw type might store types other than Integer. In this case, the compiler issues a warning message which indicates the unsafe assignment.
The test program of Fig. 18.12 uses the notion of raw type. Line 14 instantiates generic class Stack with raw type, which indicates that rawTypeStack1 can hold objects of any type. Line 17 assigns a Stack< Double > to variable rawTypeStack2, which is declared as a Stack of raw type. Line 20 assigns a Stack of raw type to Stack< Integer > variable, which is legal but causes the compiler to issue a warning message (Fig. 18.13) indicating a potentially unsafe assignmentagain, this occurs because a Stack of raw type might store types other than Integer. Also, each of the calls to generic method testPush and testPop in lines 2225 results in a compiler warning message (Fig. 18.13). These warnings occur because variables rawTypeStack1 and rawTypeStack2 are declared as Stacks of raw type, but methods testPush and testPop each expect a second argument that is a Stack with a specific type argument. The warnings indicate that the compiler cannot guarantee that the types manipulated by the stacks are the correct types, because we did not supply a variable declared with a type argument. Methods testPush (lines 3151) and testPop (lines 5474) are the same as in Fig. 18.11.
Figure 18.12. Raw type test program.
(This item is displayed on pages 891 - 893 in the print version)
1 // Fig. 18.12: RawTypeTest.java 2 // Raw type test program. 3 4 public class RawTypeTest 5 { 6 private Double[] doubleElements = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6 }; 7 private Integer[] integerElements = 8 { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; 9 10 // method to test Stacks with raw types 11 public void testStacks() 12 { 13 // Stack of raw types assigned to Stack of raw types variable 14 Stack rawTypeStack1 = new Stack( 5 ); 15 16 // Stack< Double > assigned to Stack of raw types variable 17 Stack rawTypeStack2 = new Stack< Double >( 5 ); 18 19 // Stack of raw types assigned to Stack< Integer > variable 20 Stack< Integer > integerStack = new Stack( 10 ); 21 22 testPush( "rawTypeStack1", rawTypeStack1, doubleElements ); 23 testPop( "rawTypeStack1", rawTypeStack1 ); 24 testPush( "rawTypeStack2", rawTypeStack2, doubleElements ); 25 testPop( "rawTypeStack2", rawTypeStack2 ); 26 testPush( "integerStack", integerStack, integerElements ); 27 testPop( "integerStack", integerStack ); 28 } // end method testStacks 29 30 // generic method pushes elements onto stack 31 public < T > void testPush( String name, Stack< T > stack, 32 T[] elements ) 33 { 34 // push elements onto stack 35 try 36 { 37 System.out.printf( " Pushing elements onto %s ", name ); 38 39 // push elements onto Stack 40 for ( T element : elements ) 41 { 42 System.out.printf( "%s ", element ); 43 stack.push( element ); // push element onto stack 44 } // end for 45 } // end try 46 catch ( FullStackException fullStackException ) 47 { 48 System.out.println(); 49 fullStackException.printStackTrace(); 50 } // end catch FullStackException 51 } // end method testPush 52 53 // generic method testPop pops elements from stack 54 public < T > void testPop( String name, Stack< T > stack ) 55 { 56 // pop elements from stack 57 try 58 { 59 System.out.printf( " Popping elements from %s ", name ); 60 T popValue; // store element removed from stack 61 62 // remove elements from Stack 63 while ( true ) 64 { 65 popValue = stack.pop(); // pop from stack 66 System.out.printf( "%s ", popValue ); 67 } // end while 68 } // end try 69 catch( EmptyStackException emptyStackException ) 70 { 71 System.out.println(); 72 emptyStackException.printStackTrace(); 73 } // end catch EmptyStackException 74 } // end method testPop 75 76 public static void main( String args[] ) 77 { 78 RawTypeTest application = new RawTypeTest(); 79 application.testStacks(); 80 } // end main 81 } // end class RawTypeTest
|
Figure 18.13. Warning message from the compiler.
(This item is displayed on page 894 in the print version)
|
Figure 18.13 shows the warning messages generated by the compiler (compiled with the -Xlint:unchecked option) when the file RawTypeTest.java (Fig. 18.12) is compiled. The first warning is generated for line 20, which assigned a raw type Stack to a Stack< Integer > variablethe compiler cannot ensure that all objects in the Stack will be Integer objects. The second warning is generated for line 22. Because the second method argument is a raw type Stack variable, the compiler determines the type argument for method testPush from the Double array passed as the third argument. In this case, Double is the type argument, so the compiler expects a Stack< Double > to be passed as the second argument. The warning occurs because the compiler cannot ensure that a raw type Stack contains only Double objects. The warning at line 24 occurs for the same reason, even though the actual Stack that rawTypeStack2 references is a Stack< Double >. The compiler cannot guarantee that the variable will always refer to the same Stack object, so it must use the variable's declared type to perform all type checking. Lines 23 and 25 each generate warnings because method testPop expects as an argument a Stack for which a type argument has been specified. However, in each call to testPop, we pass a raw type Stack variable. Thus, the compiler indicates a warning because it cannot check the types used in the body of the method.