Java Generics - Bounded Type Parameters

What is Bounded Type Parameters?

Suppose when we want to restrict the types that can be used as type arguments in a parameterized type. For example, a method that operates on numbers might only want to accept instances of Number or its subclasses. This is what bounded type parameters are for.
Here is the syntax for declaring Bounded type parameters.
<T extends SuperClass>
This specifies that ‘T’ can only be replaced by ‘SuperClass’ or it subclasses. Remember that extends clause is an inclusive bound. That means bound includes ‘SuperClass’ also.
Example:
<T extends Number>
Note that, in this context, extends is used in a general sense to mean either "extends" (as in classes) or "implements" (as in interfaces).

Bounded Type Parameters Example

Let's create the Generic Box class which contains a generic method inspect(U u). Note that the return type of inspect(U u) method.
public class Box<T> {

    private T t;          

    public void set(T t) {
        this.t = t;
    }

    public T get() {
        return t;
    }

    public <U extends Number> void inspect(U u){
        System.out.println("T: " + t.getClass().getName());
        System.out.println("U: " + u.getClass().getName());
    }

    public static void main(String[] args) {
        Box<Integer> integerBox = new Box<Integer>();
        integerBox.set(new Integer(10));
        integerBox.inspect("some text"); // error: this is still String!
    }
}
By modifying our generic method to include this bounded type parameter, the compilation will now fail, since our invocation of inspect() still includes a String:
Box.java:21: <U>inspect(U) in Box<java.lang.Integer> cannot
  be applied to (java.lang.String)
                        integerBox.inspect("10");
                                  ^
1 error
In addition to limiting the types, you can use to instantiate a generic type, bounded type parameters allow you to invoke methods defined in the bounds:
public class NaturalNumber<T extends Integer> {

    private T n;

    public NaturalNumber(T n)  { this.n = n; }

    public boolean isEven() {
        return n.intValue() % 2 == 0;
    }

    // ...
}
The isEven method invokes the intValue method defined in the Integer class through n.

Bounded types Parameter at Class Level Example

Let’s take an example on how to implement bounded types at class level(extend superclass) with generics.
// This class only accepts type parametes as any class
// which extends class A or class A itself.
// Passing any other type will cause compiler time error
public class GenericClass<T extends A> {
 private T t;

 public GenericClass(T t){
         this.t = t;
     }

 public void doRunTest() {
  this.t.displayClass();
 }

 public static void main(String[] args) {

  // Creating object of sub class C and
  // passing it to Bound as a type parameter.
  GenericClass<C> bec = new GenericClass<C>(new C());
  bec.doRunTest();

  // Creating object of sub class B and
  // passing it to Bound as a type parameter.
  GenericClass<B> beb = new GenericClass<B>(new B());
  beb.doRunTest();

  // similarly passing super class A
  GenericClass<A> bea = new GenericClass<A>(new A());
  bea.doRunTest();

 }
}


class A {
 public void displayClass() {
  System.out.println("Inside super class A");
 }
}

class B extends A {
 public void displayClass() {
  System.out.println("Inside sub class B");
 }
}

class C extends A {
 public void displayClass() {
  System.out.println("Inside sub class C");
 }
}
Output:
Inside sub class C
Inside sub class B
Inside super class A
Let's try to pass String type like:
GenericClass<String> dbea = new GenericClass<String>(new String());
bea.doRunTest();
The compiler will report below error:
Bound mismatch: The type String is not a valid substitute for the bounded parameter <T extends A> of the type GenericClass<T>

Multiple Bounds

The type parameter can have multiple bounds:
<T extends B1 & B2 & B3>
A type variable with multiple bounds is a subtype of all the types listed in the binding. If one of the bounds is a class, it must be specified first. For example:
Class A { /* ... */ }
interface B { /* ... */ }
interface C { /* ... */ }

class D <T extends A & B & C> { /* ... */ }
If bound A is not specified first, you get a compile-time error:

class D <T extends B & A & C> { /* ... */ }  // compile-time error

Generic Methods and Bounded Type Parameters

Bounded type parameters are key to the implementation of generic algorithms. Consider the following method that counts the number of elements in an array T[] that are greater than a specified element elem.
public static <T> int countGreaterThan(T[] anArray, T elem) {
    int count = 0;
    for (T e : anArray)
        if (e > elem)  // compiler error
            ++count;
    return count;
}
The implementation of the method is straightforward, but it does not compile because the greater than operator (>) applies only to primitive types such as short, int, double, long, float, byte, and char. You cannot use the > operator to compare objects. To fix the problem, use a type parameter bounded by the Comparable interface:
public interface Comparable<T> {
    public int compareTo(T o);
}
The resulting code will be:

public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
    int count = 0;
    for (T e : anArray)
        if (e.compareTo(elem) > 0)
            ++count;
    return count;
}

Free Spring Boot Tutorial | Full In-depth Course | Learn Spring Boot in 10 Hours


Watch this course on YouTube at Spring Boot Tutorial | Fee 10 Hours Full Course

Comments