Bounded type parameters

Bounded type parameters restrict the types that can be used as arguments for a generic type parameter. This is achieved using the extends keyword, allowing you to specify that a type parameter must be a subtype of a particular class or interface (or the class/interface itself). Bounded type parameters enhance type safety and enable access to methods defined in the bounding type.

Syntax

class ClassName<T extends BoundType> { ... }
T is the type parameter.
BoundType is the class or interface that T must extend or implement.
extends is used for both classes and interfaces in this context.Code language: JavaScript (javascript)

Types of Bounded Type Parameters

Upper-Bounded Type Parameter

An upper-bound restricts the type parameter to a specific class/interface or its subtypes.

class NumberBox<T extends Number> {
    private T number;

    public NumberBox(T number) {
        this.number = number;
    }

    public double getDoubleValue() {
        return number.doubleValue(); // Access Number's method
    }

    public static void main(String[] args) {
        NumberBox<Integer> intBox = new NumberBox<>(100);
        NumberBox<Double> doubleBox = new NumberBox<>(99.99);

        System.out.println(intBox.getDoubleValue()); // Output: 100.0
        System.out.println(doubleBox.getDoubleValue()); // Output: 99.99

        // NumberBox<String> strBox = new NumberBox<>("Test"); // Compile-time error
    }
}
Here...
T must be a Number or its subclass (e.g., Integer, Double).
Methods of Number (e.g., doubleValue()) are accessible on T.Code language: JavaScript (javascript)

Bounding to an Interface

class ComparableBox<T extends Comparable<T>> {
    private T item;

    public ComparableBox(T item) {
        this.item = item;
    }

    public int compareTo(T other) {
        return item.compareTo(other); // Access Comparable's method
    }

    public static void main(String[] args) {
        ComparableBox<String> strBox = new ComparableBox<>("Apple");
        System.out.println(strBox.compareTo("Banana")); // Output: -1 (Apple < Banana)
    }
}
Here...
T must implement Comparable<T> (e.g., String, Integer).
Methods of Comparable (e.g., compareTo) are accessible.Code language: JavaScript (javascript)

Multiple Bounds

A type parameter can have multiple bounds, combining a class and one or more interfaces using &.

Syntax
class ClassName<T extends ClassName & Interface1 & Interface2> { ... }
The class (if present) must come first, followed by interfaces.
Only one class bound is allowed, but multiple interfaces are permitted.Code language: JavaScript (javascript)
class MyClass<T extends Number & Comparable<T>> {
    private T value;

    public MyClass(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    public int compareTo(T other) {
        return value.compareTo(other);
    }

    public double doubleValue() {
        return value.doubleValue();
    }

    public static void main(String[] args) {
        MyClass<Integer> intBox = new MyClass<>(42);
        System.out.println(intBox.compareTo(50)); // Output: -1 (42 < 50)
        System.out.println(intBox.doubleValue()); // Output: 42.0
    }
}Code language: PHP (php)

Demonstrating Bounded Type Parameters

This program implements a generic Container class for storing and comparing items, a generic utility method for finding the maximum of two comparable items, and a demonstration of bounded type parameters with multiple bounds. The types used are restricted to Number and Comparable to showcase practical constraints.

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;

// Generic class with a bounded type parameter
class Container<T extends Number & Comparable<T> & Serializable> {
    private T item;

    // Constructor
    public Container(T item) {
        this.item = item;
    }

    // Get the stored item
    public T getItem() {
        return item;
    }

    // Get the double value of the item (from Number)
    public double getDoubleValue() {
        return item.doubleValue();
    }

    // Compare with another item (from Comparable)
    public int compareTo(T other) {
        return item.compareTo(other);
    }

    // Check if the item is serializable (from Serializable)
    public boolean isSerializable() {
        return item instanceof Serializable;
    }
}

// Utility class with generic methods
class Util {
    // Generic method with bounded type parameter to find the maximum
    public static <T extends Comparable<T>> T findMax(T item1, T item2) {
        return item1.compareTo(item2) >= 0 ? item1 : item2;
    }

    // Generic method to print a list of numbers
    public static <T extends Number> void printNumberList(List<T> list, String label) {
        System.out.print(label + ": ");
        for (T num : list) {
            System.out.print(num + " ");
        }
        System.out.println();
    }
}

public class BoundedTypeDemo {
    public static void main(String[] args) {
        // Example 1: Using Container with Integer
        Container<Integer> intContainer = new Container<>(42);
        System.out.println("Integer Container:");
        System.out.println("Item: " + intContainer.getItem());
        System.out.println("Double Value: " + intContainer.getDoubleValue());
        System.out.println("Compared to 50: " + intContainer.compareTo(50)); // Negative if 42 < 50
        System.out.println("Is Serializable: " + intContainer.isSerializable());
        System.out.println();

        // Example 2: Using Container with Double
        Container<Double> doubleContainer = new Container<>(99.99);
        System.out.println("Double Container:");
        System.out.println("Item: " + doubleContainer.getItem());
        System.out.println("Double Value: " + doubleContainer.getDoubleValue());
        System.out.println("Compared to 100.0: " + doubleContainer.compareTo(100.0)); // Negative if 99.99 < 100.0
        System.out.println("Is Serializable: " + doubleContainer.isSerializable());
        System.out.println();

        // Example 3: Using findMax method
        System.out.println("Maximum Examples:");
        System.out.println("Max of 10 and 20: " + Util.findMax(10, 20));
        System.out.println("Max of 3.14 and 2.71: " + Util.findMax(3.14, 2.71));
        System.out.println("Max of 'Apple' and 'Banana': " + Util.findMax("Apple", "Banana"));
        System.out.println();

        // Example 4: Using printNumberList with bounded lists
        List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5);
        List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);
        Util.printNumberList(intList, "Integer List");
        Util.printNumberList(doubleList, "Double List");
        // Util.printNumberList(Arrays.asList("A", "B"), "String List"); // Compile-time error

        // Example 5: Compile-time error demonstration
        // Container<String> strContainer = new Container<>("Test"); // Error: String is not a Number
    }
}

/*
Integer Container:
Item: 42
Double Value: 42.0
Compared to 50: -8
Is Serializable: true

Double Container:
Item: 99.99
Double Value: 99.99
Compared to 100.0: -1
Is Serializable: true

Maximum Examples:
Max of 10 and 20: 20
Max of 3.14 and 2.71: 3.14
Max of 'Apple' and 'Banana': Banana

Integer List: 1 2 3 4 5 
Double List: 1.1 2.2 3.3 
*/

Benefits of Bounded Type Parameters

  • Type Safety: Restricts types to valid ones, preventing errors at compile time.
  • Access to Methods: Allows access to methods of the bounding type (e.g., doubleValue() for Number).
  • Flexibility: Enables generic code to work with a family of related types (e.g., all Number subclasses).
Scroll to Top