If the operations performed by several overloaded methods are identical for each argument type, the overloaded methods can be more compactly and conveniently coded using a generic method. You can write a single generic method declaration that can be called with arguments of different types. Based on the types of the arguments passed to the generic method, the compiler handles each method call appropriately.
Figure 18.3 reimplements the application of Fig. 18.1 using a generic printArray method (lines 714). Note that the printArray method calls in lines 24, 26 and 28 are identical to those of Fig. 18.1 (lines 44, 46 and 48) and that the outputs of the two applications are identical. This dramatically demonstrates the expressive power of generics.
Figure 18.3. Printing array elements using generic method printArray.
(This item is displayed on page 874 in the print version)
1 // Fig. 18.3: GenericMethodTest.java 2 // Using generic methods to print array of different types. 3 4 public class GenericMethodTest 5 { 6 // generic method printArray 7 public static < E > void printArray( E[] inputArray ) 8 { 9 // display array elements 10 for ( E element : inputArray ) 11 System.out.printf( "%s ", element ); 12 13 System.out.println(); 14 } // end method printArray 15 16 public static void main( String args[] ) 17 { 18 // create arrays of Integer, Double and Character 19 Integer[] intArray = { 1, 2, 3, 4, 5 }; 20 Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7 }; 21 Character[] charArray = { 'H', 'E', 'L', 'L', 'O' }; 22 23 System.out.println( "Array integerArray contains:" ); 24 printArray( integerArray ); // pass an Integer array 25 System.out.println( " Array doubleArray contains:" ); 26 printArray( doubleArray ); // pass a Double array 27 System.out.println( " Array characterArray contains:" ); 28 printArray( characterArray ); // pass a Character array 29 } // end main 30 } // end class GenericMethodTest
|
Line 7 begins method printArray's declaration. All generic method declarations have a type parameter section delimited by angle brackets (< and >) that precedes the method's return type ( < E > in this example). Each type parameter section contains one or more type parameters (also called formal type parameters), separated by commas. A type parameter, also known as a type variable, is an identifier that specifies a generic type name. The type parameters can be used to declare the return type, parameter types and local variable types in a generic method declaration, and act as placeholders for the types of the arguments passed to the generic method, which are known as actual type arguments. A generic method's body is declared like that of any other method. Note that type parameters can represent only reference typesnot primitive types (like int, double and char). Note, too, that the type parameter names throughout the method declaration must match those declared in the type parameter section. For example, line 10 declares element as type E, which matches the type parameter (E) declared in line 7. Also, a type parameter can be declared only once in the type parameter section but can appear more than once in the method's parameter list. For example, the type parameter name E appears twice in the following method's parameter list:
public static < E > void printTwoArrays( E[] array1, E[] array2 )
Type parameter names need not be unique among different generic methods.
Common Programming Error 18.1
When declaring a generic method, failing to place a type parameter section before the return type of a method is a syntax errorthe compiler will not understand the type parameter name when it is encountered in the method. |
Method printArray's type parameter section declares type parameter, E, as the placeholder for the array element type that printArray will output. Note that E appears in the parameter list as the array element type (line 7). The for statement header (line 10) also uses E as the element type. These are the same two locations where the overloaded printArray methods of Fig. 18.1 specified Integer, Double or Character as the array element type. The remainder of printArray is identical to the versions presented in Fig. 18.1.
Good Programming Practice 18.1
It is recommended that type parameters be specified as individual capital letters. Typically, a type parameter that represents the type of an element in an array (or other collection) is named E for "element." |
As in Fig. 18.1, the program begins by declaring and initializing six-element Integer array integerArray (line 19), seven-element Double array doubleArray (line 20) and five-element Character array characterArray (line 21). Then the program outputs each array by calling printArray (lines 24, 26 and 28)once with argument integerArray, once with argument doubleArray and once with argument characterArray.
When the compiler encounters line 24, it first determines argument integerArray's type (i.e., Integer[]) and attempts to locate a method named printArray that specifies a single Integer[] parameter. There is no such method in this example. Next, the compiler determines whether there is a generic method named printArray that specifies a single array parameter and uses a type parameter to represent the array element type. The compiler determines that method printArray (lines 714) is a match and sets up a call to the method. The same process is repeated for the calls to method printArray at lines 26 and 28.
Common Programming Error 18.2
If the compiler cannot match a method call to a non-generic or a generic method declaration, a compilation error occurs. |
Common Programming Error 18.3
If the compiler does not find a method declaration that matches a method call exactly, but does find two or more generic methods that can satisfy the method call, a compilation error occurs. |
In addition to setting up the method calls, the compiler also determines whether the operations in the method body can be applied to elements of the type stored in the array argument. The only operation performed on the array elements in this example is to output the string representation of the elements. Line 11 performs an implicit toString call on every element. To work with generics, every element of the array must be an object of a class or interface type. Since all objects have a toString method, the compiler is satisfied that line 11 performs a valid operation for any object in printArray's array argument. The toString methods of classes Integer, Double and Character return the string representation of the underlying int, double or char value, respectively.
When the compiler translates generic method printArray into Java bytecodes, it removes the type parameter section and replaces the type parameters with actual types. This process is known as erasure. By default all generic types are replaced with type Object. So the compiled version of method printArray appears as shown in Fig. 18.4there is only one copy of this code that is used for all printArray calls in the example. This is quite different from other, similar mechanisms, such as C++'s templates in which a separate copy of the source code is generated and compiled for every type passed as an argument to the method. As we will discuss in Section 18.4, the translation and compilation of generics is a bit more involved than what we have discussed in this section.
Figure 18.4. Generic method printArray after erasure is performed by the compiler.
(This item is displayed on page 876 in the print version)
1 public static void printArray( Object[] inputArray ) 2 { 3 // display array elements 4 for ( Object element : inputArray ) 5 System.out.printf( "%s ", element ); 6 7 System.out.println(); 8 } // end method printArray |
By declaring printArray as a generic method in Fig. 18.3, we eliminated the need for the overloaded methods of Fig. 18.1, saving 20 lines of code and creating a reusable method that can output the string representations of the elements in any array that contains objects. However, this particular example could have simply declared the printArray method as shown in Fig. 18.4 using an Object array as the parameter. This would have yielded the same results because any Object can be output as a String. In a generic method, the benefits become apparent when the method also uses a type parameter as the method's return type, as we demonstrate in the next section.
Introduction to Computers, the Internet and the World Wide Web
Introduction to Java Applications
Introduction to Classes and Objects
Control Statements: Part I
Control Statements: Part 2
Methods: A Deeper Look
Arrays
Classes and Objects: A Deeper Look
Object-Oriented Programming: Inheritance
Object-Oriented Programming: Polymorphism
GUI Components: Part 1
Graphics and Java 2D™
Exception Handling
Files and Streams
Recursion
Searching and Sorting
Data Structures
Generics
Collections
Introduction to Java Applets
Multimedia: Applets and Applications
GUI Components: Part 2
Multithreading
Networking
Accessing Databases with JDBC
Servlets
JavaServer Pages (JSP)
Formatted Output
Strings, Characters and Regular Expressions
Appendix A. Operator Precedence Chart
Appendix B. ASCII Character Set
Appendix C. Keywords and Reserved Words
Appendix D. Primitive Types
Appendix E. (On CD) Number Systems
Appendix F. (On CD) Unicode®
Appendix G. Using the Java API Documentation
Appendix H. (On CD) Creating Documentation with javadoc
Appendix I. (On CD) Bit Manipulation
Appendix J. (On CD) ATM Case Study Code
Appendix K. (On CD) Labeled break and continue Statements
Appendix L. (On CD) UML 2: Additional Diagram Types
Appendix M. (On CD) Design Patterns
Appendix N. Using the Debugger
Inside Back Cover