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).