Skip to Content
Course content

62: Bounded Type Parameters

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're hiring for a position at your company. If you put out a job ad saying, "I'm looking for a Person," anyone can apply. You'll get poets, chefs, and accountants. But the problem is, once they're hired, you can't actually ask them to do anything specific to the job—like "perform surgery" or "write a legal brief"—because a generic Person doesn't necessarily have those skills. To get someone who can actually do the work, you have to change the requirement: "I'm looking for a Person who is a Doctor."

That's exactly what's happening with Bounded Type Parameters in Java. When we use a plain generic like <T>, Java treats T as an Object. And while every class inherits from Object, Object is pretty boring; it doesn't have the specialized methods you actually need to get work done.

Getting more out of T

Let's look at a scenario I've run into a dozen times. Say you want to write a method that finds the larger of two numbers. If you try to do it with a standard generic, you'll hit a wall immediately:

public <T> T findMax(T a, T b) {
    // Error: The operator > is undefined for the type T
    return (a > b) ? a : b; 
}

Java is complaining because T could be anything—a String, a Scanner, or a UserAccount. You can't use the > operator on a Scanner. To fix this, we need to put a "bound" on the type. We need to tell Java, "I don't care exactly what T is, as long as it implements the Comparable interface."

Here is how we map that "Doctor" requirement to the code:

public <T extends Comparable<T>> T findMax(T a, T b) {
    return (a.compareTo(b) > 0) ? a : b;
}

By adding extends Comparable<T>, we've told the compiler that T is guaranteed to have the compareTo method. Now, the code compiles, and we can pass in Integers, Doubles, or even custom Employee objects, provided they implement Comparable.

The Ceiling and the Floor

Most of the time, you'll use Upper Bounds (the extends keyword). I think of this as setting a "ceiling." You're saying the type must be the specified class or any subclass of it. If you use <T extends Number>, you've capped it at Number. You can use Integer or Double, but you can't use String.

Then there's the weird sibling: Lower Bounds. This uses the super keyword. You'll rarely see this in a class definition (in fact, you can't use it there), but you'll see it in method arguments. It’s used when you want to write to a collection. If you have a List<? super Integer>, you're saying "this list can hold Integers or any superclass of Integer (like Number or Object)."

I'll be honest: lower bounds can feel backward at first. Just remember this rule of thumb I use: Use extends when you want to get something out of a structure and use its methods; use super when you want to put something into a structure.

Mixing and Matching Bounds

Sometimes, one bound isn't enough. What if you need a type that is both a Number and Comparable? Java lets you use an ampersand (&) for this. The only catch is that the class must come first, followed by the interfaces.

public <T extends Number & Comparable<T>> void processNumericValue(T value) {
    // Now I can call .doubleValue() AND .compareTo()
}

It's a bit wordy, sure, but it gives you total control over the capabilities of your generic types without sacrificing type safety.




📋 Practical Task

Build a Weight-Based Shipping Calculator

You need to create a shipping system that calculates the total weight of a shipment. However, not every object in your system has a weight (e.g., a DigitalDownload doesn't, but a PhysicalBook does).

Your task:

  • Create an interface called Weightable with a method double getWeight().
  • Create two classes, PhysicalBook and Laptop, that both implement Weightable.
  • Create a class called ShippingContainer with a generic method called calculateTotalWeight.
  • Crucially: Use a bounded type parameter in calculateTotalWeight to ensure the method only accepts a List of items that are Weightable. If the method is called with a list of objects that aren't Weightable, it should fail at compile time.
  • In your main method, instantiate a list of PhysicalBook objects and use your ShippingContainer to find their total weight.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.