Bounded Types

There are some situations where the generics have to take some types of data only this is done by using Bounded types by restricting some types of data.

Ex: To calculate the average of elements in an array where we can calculate on integer, float means numerical not on String.

// 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 to
 // an 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;
 }
}

It will throw error as it calculate the average of numerical numbers as double but

  • compiler don't know that it is only for numeric data types as Integer, Double, Float.
  • So it throws error at doubleValue() as we have to mention that generic is only for numeric data types.
  • Java provides bounded types. When specifying a type parameter, we 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.

<T extends SuperClass>

// 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.
public class Simple 
{
 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);
 }
}

Output:

iob average is 3.0

dob average is 3.3

Using Wildcard Arguments

  • There will be some situations where we have to compare the invoking object with the argument parameters like to compare the average values of different data type objects as Integer and Double.
// This won't work!
// Determine if two averages are the same.
boolean sameAvg(Stats<T> ob) {
 if(average() == ob.average())
 return true;
 return false;
}
  • This will work for same object type as Stats<Integer> and the argument parameter of Stats<Integer> but not Stats<Double>.

  • To achieve this we have to use wildcard argument which is denoted by ?

// Determine if two averages are the same.
// Notice the use of the wildcard.
boolean sameAvg(Stats<?> ob) {
 if(average() == ob.average())
 return true;
 return false;
}

Example

// 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;
 }
// Determine if two averages are the same.
// Notice the use of the wildcard.
boolean sameAvg(Stats<?> ob) {
 if(average() == ob.average())
 return true;
 return false;
}
}
// Demonstrate Stats.
public class Simple 
{
 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);

Double dnums2[] = { 1.0, 2.0, 3.0, 4.0, 5.0 };
 Stats<Double> dob2 = new Stats<Double>(dnums2);
 double w2 = dob2.average();
 System.out.println("dob average is " + w2);

// See which arrays have same average.
 System.out.print("Averages of iob and dob ");
 if(iob.sameAvg(dob))
 System.out.println("are the same.");
 else
 System.out.println("differ.");

// See which arrays have same average.
 System.out.print("Averages of iob and dob ");
 if(iob.sameAvg(dob2))
 System.out.println("are the same.");
 else
 System.out.println("differ.");
 }
}

Output:

iob average is 3.0

dob average is 3.3

dob average is 3.0

Averages of iob and dob differ.

Averages of iob and dob are the same.

Bounded Wildcards

  • We can bound the wildcards same as arguments.
  • A bounded wildcard is especially important when you are creating a generic type that will operate on a class hierarchy.
// Bounded Wildcard arguments.
// Two-dimensional coordinates.
class TwoD {
 int x, y;
 TwoD(int a, int b) {
 x = a;
 y = b;
 }
}
// Three-dimensional coordinates.
class ThreeD extends TwoD {
 int z;
 ThreeD(int a, int b, int c) {
 super(a, b);
 z = c;
 }
}
// Four-dimensional coordinates.
class FourD extends ThreeD {
 int t;
 FourD(int a, int b, int c, int d) {
 super(a, b, c);
 t = d;
 }
}
// This class holds an array of coordinate objects.
class Coords<T extends TwoD> {
 T[] coords;
 Coords(T[] o) { coords = o; }
}

// Demonstrate a bounded wildcard.
public class Simple
{
 static void showXY(Coords<?> c) {
 System.out.println("X Y Coordinates:");
 for(int i=0; i < c.coords.length; i++)
 System.out.println(c.coords[i].x + " " +
 c.coords[i].y);
 System.out.println();
 }
 static void showXYZ(Coords<? extends ThreeD> c) {
 System.out.println("X Y Z Coordinates:");
 for(int i=0; i < c.coords.length; i++)
System.out.println(c.coords[i].x + " " +
 c.coords[i].y + " " +
 c.coords[i].z);
 System.out.println();
 }
 static void showAll(Coords<? extends FourD> c) {
 System.out.println("X Y Z T Coordinates:");
 for(int i=0; i < c.coords.length; i++)
 System.out.println(c.coords[i].x + " " +
 c.coords[i].y + " " +
 c.coords[i].z + " " +
 c.coords[i].t);
 System.out.println();
 }
 public static void main(String args[]) {
 TwoD td[] = {
 new TwoD(0, 0),
 new TwoD(7, 9),
 new TwoD(18, 4),
 new TwoD(-1, -23)
 };
 Coords<TwoD> tdlocs = new Coords<TwoD>(td);
 System.out.println("Contents of tdlocs.");
 showXY(tdlocs); // OK, is a TwoD
// showXYZ(tdlocs); // Error, not a ThreeD
// showAll(tdlocs); // Error, not a FourD
 // Now, create some FourD objects.
 FourD fd[] = {
 new FourD(1, 2, 3, 4),
 new FourD(6, 8, 14, 8),
 new FourD(22, 9, 4, 9),
 new FourD(3, -2, -23, 17)
 };
 Coords<FourD> fdlocs = new Coords<FourD>(fd);
 System.out.println("Contents of fdlocs.");
 // These are all OK.
 showXY(fdlocs);
 showXYZ(fdlocs);
 showAll(fdlocs);
 }
}

Output:

Contents of tdlocs.

X Y Coordinates:

0 0

7 9

18 4

-1 -23

Contents of fdlocs.

X Y Coordinates:

1 2

6 8

22 9

3 -2

X Y Z Coordinates:

1 2 3

6 8 14

22 9 4

3 -2 -23

X Y Z T Coordinates:

1 2 3 4

6 8 14 8

22 9 4 9

3 -2 -23 17

results matching ""

    No results matching ""