All Rights Reserved. 20.2 Motivation for Generic Methods 20.3 Generic Methods: Implementation and Compile-Time Translation 20.4 Additional Compile-Time Translation Issues: Methods That Use a Type Parameter as the Return Type 20.5 Overloading Generic Methods 20.6 Generic Classes
All Rights Reserved. Overloaded methods are often used to perform similar operations on different types of data. Study each printArray method (Fig. 20.1).
◦ Note that the array element type appears in each method’s
header and for-statement header. ◦ If we were to replace the element types in each method with a generic name—T by convention—then all three methods would look like the one in Fig. 20.2.
Education, Inc. All Rights Reserved. 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. 20.4—there is only one copy of this code, which is used for all printArray calls in the example.
Education, Inc. All Rights Reserved. Comparable<T> objects have a compareTo method. ◦ The method must return 0 if the objects are equal, ◦ a negative integer if object1 is less than object2 ◦ or a positive integer if object1 is greater than object2. A benefit of implementing interface Comparable<T> is that Comparable<T> objects can be used with the sorting and searching methods of class Collections (package java.util).
Education, Inc. All Rights Reserved. When the compiler translates generic method maximum into Java bytecodes, it uses erasure to replace the type parameters with actual types. All type parameters are replaced with the upper bound of the type parameter, which is specified in the type- parameter section. When the compiler replaces the type-parameter information with the upper-bound type in the method declaration, it also inserts explicit cast operations in front of each method call to ensure that the returned value is of the type expected by the caller.
Education, Inc. All Rights Reserved. The concept of a data structure, such as a stack, can be understood independently of the element type it manipulates. Generic classes provide a means for describing the
concept of a stack (or any other class) in a type-
independent manner. These classes are known as parameterized classes or
parameterized types because they accept one or more
Education, Inc. All Rights Reserved. The code in methods testPushDouble and testPushInteger from the previous example is almost identical for pushing values onto a Stack<Double> or a Stack<Integer>, Respectively, and the code in methods
testPopDouble and testPopInteger is almost
identical for popping values from a Stack<Double> or a Stack<Integer>, respectively. This presents another opportunity to use generic
Education, Inc. All Rights Reserved. It’s also possible to instantiate generic class Stack without specifying a type argument, as follows: // no type-argument specified Stack objectStack = new Stack(new Double[5]); ◦ objectStack has a raw type ◦ The compiler implicitly uses type Object throughout the generic class for each type argument. ◦ The preceding statement creates a Stack that can store objects of any type. ◦ Important for backward compatibility with prior Java versions. ◦ Raw-type operations are unsafe and could lead to exceptions.
Education, Inc. All Rights Reserved. Figure 20.12 shows the warning messages generated by the compiler when the file RawTypeTest.java (Fig. 20.11) is compiled with the -Xlint:unchecked option, which provides more information about potentially unsafe operations in code that uses generics.
Suppose that you’d like to implement a generic method sum
that totals the numbers in an ArrayList . (Fig. 20.13) ◦ You’d begin by inserting the numbers in the ArrayList . ◦ The numbers would be autoboxed as objects of the type-wrapper classes —any int value would be autoboxed as an Integer object, and any double value would be autoboxed as a Double object. ◦ We’d like to be able to total all the numbers in the ArrayList regardless of their type. ◦ For this reason, we’ll declare the ArrayList with the type argument Number, which is the superclass of both Integer and Double. ◦ In addition, method sum will receive a parameter of type ArrayList<Number> and total its elements.
Education, Inc. All Rights Reserved. Given that method sum can total the elements of an ArrayList of Numbers, you might expect that the method would also work for ArrayLists that contain elements of only one numeric type, such as ArrayList<Integer>. Modified class TotalNumbers to create an ArrayList- of Integers and pass it to method sum. When we compile the program, the compiler issues the following error message: sum(java.util.ArrayList<java.lang.Number>) in TotalNumbersErrors cannot be applied to (java.util.ArrayList<java.lang.Integer>) Although Number is the superclass of Integer, the compiler doesn’t consider the parameterized type ArrayList<Number> to be a superclass of ArrayList<Integer>.
Education, Inc. All Rights Reserved. To create a more flexible version of the sum method that can total the elements of any ArrayList containing elements of any subclass of Number we use wildcard-type arguments. Wildcards enable you to specify method parameters, return values, variables or fields, and so on, that act as supertypes or subtypes of parameterized types. In Fig. 20.14, method sum’s parameter is declared in line 50 with the type: ArrayList<? extends Number> A wildcard-type argument is denoted by a question mark (?), which by itself represents an “unknown type.” ◦ In this case, the wildcard extends class Number, which means that the wildcard has an upper bound of Number. ◦ Thus, the unknown-type argument must be either Number or a subclass of Number.