Chapter: Java The Complete Reference - The Java Language - Generics

Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail

Bounded Types

In the preceding examples, the type parameters could be replaced by any class type.

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.

 

Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail


Copyright © 2018-2020 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.