Bounded Type Parameters
Part of the Object Oriented Programming section of Coddy's Java journey — lesson 56 of 87.
So far, our generic type parameters can accept any type. But what if you need to restrict them?
For example, a method that calculates the sum of numbers shouldn't accept strings. Bounded type parameters let you limit which types can be used with your generics.
Use the extends keyword to set an upper bound on a type parameter:
class NumberBox<T extends Number> {
private T value;
public NumberBox(T value) {
this.value = value;
}
public double getDoubleValue() {
return value.doubleValue(); // Can call Number methods!
}
}
NumberBox<Integer> intBox = new NumberBox<>(42);
NumberBox<Double> doubleBox = new NumberBox<>(3.14);
// NumberBox<String> strBox = new NumberBox<>("Hi"); // Compile error!The bound T extends Number means T must be Number or any of its subclasses (Integer, Double, etc.). This also lets you call methods defined in Number on the type parameter.
You can specify multiple bounds using &. If one bound is a class, it must come first:
class DataProcessor<T extends Number & Comparable<T>> {
public boolean isGreater(T a, T b) {
return a.compareTo(b) > 0;
}
}Here, T must extend Number AND implement Comparable. Bounded type parameters work with generic methods too:
public static <T extends Comparable<T>> T findMax(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
String result = findMax("apple", "banana"); // Returns "banana"Bounded type parameters make your generics more powerful by ensuring type safety while enabling access to specific methods of the bound type.
Challenge
EasyLet's build a statistics calculator that only works with numeric types! You'll use bounded type parameters to ensure your calculator can only process numbers, while gaining access to useful methods from the Number class.
You'll organize your code across three files:
NumberStats.java: Create a generic class that calculates statistics for numeric values. YourNumberStats<T extends Number>class should store two private fields of typeT:value1andvalue2. Include a constructor that accepts both values. Add a methodgetSum()that returns the sum of both values as adouble(use thedoubleValue()method inherited from Number). Also add a methodgetAverage()that returns the average of the two values as adouble.ComparableBox.java: Create a generic class with multiple bounds that can find the larger of two values. YourComparableBox<T extends Number & Comparable<T>>class should store two private fields of typeT:firstandsecond. Include a constructor that accepts both values. Add a methodgetMax()that returns the larger value usingcompareTo(). Also add a methodgetMaxAsDouble()that returns the larger value converted to adouble.Main.java: Bring your bounded generics to life! You'll receive four inputs: two integers and two doubles.First, create a
NumberStats<Integer>with the two integer values. Print the sum and average:Integer sum: [sum] Integer average: [average]Next, create a
NumberStats<Double>with the two double values. Print the sum and average:Double sum: [sum] Double average: [average]Finally, create a
ComparableBox<Integer>with the two integer values and print:Max integer: [max] Max as double: [maxAsDouble]
You will receive four inputs in order: first integer, second integer, first double, second double.
Notice how the bound T extends Number lets you call doubleValue() on your generic type—something you couldn't do with an unbounded type parameter. And the multiple bounds in ComparableBox ensure you can both convert to double AND compare values!
Try it yourself
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Read inputs
int int1 = scanner.nextInt();
int int2 = scanner.nextInt();
double double1 = scanner.nextDouble();
double double2 = scanner.nextDouble();
// TODO: Create a NumberStats<Integer> with the two integer values
// Print the sum and average in the format:
// Integer sum: [sum]
// Integer average: [average]
// TODO: Create a NumberStats<Double> with the two double values
// Print the sum and average in the format:
// Double sum: [sum]
// Double average: [average]
// TODO: Create a ComparableBox<Integer> with the two integer values
// Print the max and max as double in the format:
// Max integer: [max]
// Max as double: [maxAsDouble]
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe this KeywordMethodsFields (Attributes)Constructor MethodConstructor OverloadingRecap - Simple Calculator4Inheritance
Basic Inheritance (extends)The super KeywordMethod Overriding (@Override)Constructor ChainingThe Object ClassSingle & Multilevel InheritWhy No Multi Class InheritRecap - Employee Hierarchy7Special Methods & Object Class
toString() Methodequals() and hashCode()clone() MethodcompareTo() and ComparableComparator InterfaceRecap - Custom Sorting2Access Modifiers & Encapsulate
Access Levels OverviewGetter and Setter MethodsInformation HidingThe final KeywordRecap - Bank Account Manager5Polymorphism
Method Overloading BasicsMethod Overriding (Run-Time)Upcasting and DowncastingThe instanceof OperatorAbstract Classes and MethodsRecap - Shape Calculator8Advanced OOP Concepts
Composition vs InheritanceAggregation vs CompositionInner Nested & Anonymous ClassEnums and Enum MethodsRecords (Java 16+)Sealed Classes (Java 17+)3Class Props & Static Member
Instance vs Static VariablesStatic MethodsStatic BlocksConstants (static final)Recap - Counter & Utility6Interfaces & Abstract Classes
Introduction to InterfacesImplementing InterfacesMulti Interface ImplemenDefault & Static in InterfaceAbstract Classes vs InterfacesFunctional InterfacesRecap - Payment System9Generics
Introduction to GenericsGeneric ClassesGeneric MethodsBounded Type ParametersWildcards (?, extends, super)Recap - Generic Container