Bounded
Types
In the preceding examples,
the type parameters could be replaced by any class type. This is fine for many
purposes, but sometimes it is useful to limit the types that can be passed to a
type parameter. For example, assume that you want to create a generic class
that contains a method that returns the average of an array of numbers.
Furthermore, you want to use the class to obtain the average of an array of any
type of number, including integers, floats,
and doubles. Thus, you want to
specify the type of the numbers generically, using a type parameter. To create such a class, you might try
something like this:
//Stats attempts (unsuccessfully) to create a
generic class that can compute the average of an array of numbers of any given
type.
// The class contains an error!
class Stats<T> {
T[] nums; // nums is an array of type T
//Pass the constructor a reference toan array
of type T.
Stats(T[] o) { nums = o;
}
// Return type double in all cases.
double average() {
double sum = 0.0;
for(int i=0; i < nums.length; i++)
sum += nums[i].doubleValue(); // Error!!!
return sum / nums.length;
}
}
In Stats, the average( )
method attempts to obtain the double
version of each number in the nums
array by calling doubleValue( ).
Because all numeric classes, such as Integer
and Double, are subclasses of Number, and Number defines the
doubleValue( ) method, this method
is available to all numeric wrapper classes. The trouble is that the compiler
has no way to know that you are intending to create Stats objects using only numeric types. Thus, when you try to
compile Stats, an error is reported
that indicates that the doubleValue( )
method is unknown. To solve this problem, you need some way to tell the
compiler that you intend to pass only numeric types to T. Furthermore, you need some way to ensure that only numeric
types are actually passed.
To handle such situations,
Java provides bounded types. When
specifying a type parameter, you can create an upper bound that declares the
superclass from which all type arguments must be derived. This is accomplished
through the use of an extends clause
when specifying the type parameter, as shown here:
<T extends superclass>
This specifies that T can only be replaced by superclass, or subclasses of superclass. Thus, superclass defines an inclusive, upper limit.
You can use an upper bound to
fix the Stats class shown earlier by
specifying Number as an upper bound,
as shown here:
//In this version of Stats, the type argument
for T must be either Number, or a class derived from Number.
class Stats<T extends Number> {
T[] nums; // array of Number or subclass
//Pass the constructor a reference to an array
of type Number or subclass.
Stats(T[] o) {
nums = o;
}
//Return type double in all cases.
double average() {
double sum = 0.0;
for(int i=0; i < nums.length; i++)
sum += nums[i].doubleValue();
return sum / nums.length;
}
}
// Demonstrate Stats.
class BoundsDemo {
public static void main(String args[]) {
Integer inums[] = { 1, 2, 3, 4, 5 };
Stats<Integer> iob = new
Stats<Integer>(inums);
double v = iob.average();
System.out.println("iob average is "
+ v);
Double dnums[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
Stats<Double> dob = new
Stats<Double>(dnums);
double w = dob.average();
System.out.println("dob average is "
+ w);
This won't compile because String is not a
subclass of Number.
String strs[] = { "1", "2",
"3", "4", "5" };
Stats<String> strob = new
Stats<String>(strs);
double x = strob.average();
System.out.println("strob average is
" + v);
}
}
The output is shown here:
Average is 3.0
Average is 3.3
Notice how Stats is now declared by this line:
class Stats<T extends Number> {
Because the type T is
now bounded by Number, the Java
compiler knows that all objects of type T
can call doubleValue( ) because it
is a method declared by Number. This
is, by itself, a major advantage. However, as an added bonus, the bounding of T also prevents nonnumeric Stats objects from being created. For
example, if you try removing the comments from the lines at the end of the
program, and then try recompiling, you will receive compile-time errors because
String is not a subclass of Number.
In addition to using a class
type as a bound, you can also use an interface type. In fact, you can specify
multiple interfaces as bounds. Furthermore, a bound can include both a class
type and one or more interfaces. In this case, the class type must be specified
first. When a bound includes an interface type, only type arguments that
implement that interface are legal. When specifying a bound that has a class
and an interface, or multiple interfaces, use the & operator to connect them. For example,
class Gen<T extends MyClass &
MyInterface> { // ...
Here, T is bounded by a class called MyClass
and an interface called MyInterface.
Thus, any type argument passed to T
must be a subclass of MyClass and
implement MyInterface.
Related Topics
Privacy Policy, Terms and Conditions, DMCA Policy and Compliant
Copyright © 2018-2023 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.