Lesson Completion
Back to course

Bounded Type Parameters: Restricting Generic Types

Advanced
15 minutes4.7Java

1. The Hook (The "Byte-Sized" Intro)

  • In a Nutshell: Bounded type parameters restrict what types can be used as type arguments.
  • Syntax: <T extends ClassName> (upper bound). Now you can call methods from the bound type! Example: <T extends Number> allows T.doubleValue().
  • Multiple bounds: <T extends Class & Interface> (class first, then interfaces with &). Bounds enable generic algorithms that need specific capabilities.

Think of licensed driver. Unrestricted <T> = anyone can drive. Bounded <T extends LicenseHolder> = only licensed drivers. You know they have required skills!


2. Conceptual Clarity (The "Simple" Tier)

💡 The Analogy: The Job Requirement

  • Unbounded <T>: Hire anyone for any job
  • Bounded <T extends Programmer>: Hire only programmers (can call .code())

3. Technical Mastery (The "Deep Dive")

Upper Bound Syntax

java
// T must be Number or its subclass public class Calculator<T extends Number> { private T value; public double getDoubleValue() { return value.doubleValue(); // ✅ Can call Number methods! } } Calculator<Integer> calc1 = new Calculator<>(); // ✅ Integer extends Number Calculator<Double> calc2 = new Calculator<>(); // ✅ Double extends Number // Calculator<String> calc3 = new Calculator<>(); // ❌ String doesn't extend Number

Multiple Bounds

java
// T must extend both Comparable AND Serializable public <T extends Comparable<T> & Serializable> void process(T item) { // Can call compareTo() and serialize }

4. Interactive & Applied Code

java
import java.io.Serializable; import java.util.*; // Single bound: Number class Calculator<T extends Number> { private T num1; private T num2; public Calculator(T num1, T num2) { this.num1 = num1; this.num2 = num2; } // Can call Number methods! public double sum() { return num1.doubleValue() + num2.doubleValue(); } public double product() { return num1.doubleValue() * num2.doubleValue(); } } // Multiple bounds class DataProcessor { // T must implement BOTH Comparable and Serializable public static <T extends Comparable<T> & Serializable> T max(T a, T b) { return a.compareTo(b) > 0 ? a : b; // Can call compareTo() } // Bounded wildcard in method public static double sumList(List<? extends Number> list) { double sum = 0; for (Number num : list) { sum += num.doubleValue(); // Works because extends Number } return sum; } } public class BoundedTypeDemo { public static void main(String[] args) { // Calculator with different Number types Calculator<Integer> intCalc = new Calculator<>(10, 20); System.out.println("Integer sum: " + intCalc.sum()); Calculator<Double> doubleCalc = new Calculator<>(3.14, 2.86); System.out.println("Double product: " + doubleCalc.product()); // Calculator<String> stringCalc = new Calculator<>("A", "B"); // ❌ Compile error! // Multiple bounds String max1 = DataProcessor.max("Apple", "Banana"); System.out.println("Max string: " + max1); Integer max2 = DataProcessor.max(10, 20); System.out.println("Max integer: " + max2); // Bounded wildcard List<Integer> ints = Arrays.asList(1, 2, 3, 4, 5); List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3); System.out.println("Sum of ints: " + DataProcessor.sumList(ints)); System.out.println("Sum of doubles: " + DataProcessor.sumList(doubles)); } // Generic method with bound public static <T extends Comparable<T>> T findMax(List<T> list) { if (list.isEmpty()) return null; T max = list.get(0); for (T item : list) { if (item.compareTo(max) > 0) { max = item; } } return max; } }

5. The Comparison & Decision Layer

Bound TypeSyntaxUse Case
Unbounded<T>No restrictions needed
Upper bound<T extends Number>Need specific methods
Multiple bounds<T extends Class & I1 & I2>Need multiple capabilities

6. The "Interview Corner" (The Edge)

The "Killer" Interview Question: "Why can't you have <T extends String>?" Answer: String is final—cannot be extended! Bounded types must allow subclasses. Using <T extends String> is pointless—just use String directly!

java
// ❌ Pointless: String is final public <T extends String> void process(T str) { // T can ONLY be String (no subclasses possible) } // ✅ Just use String! public void process(String str) { // Same effect, no generics needed }

Valid bounds: Classes that can be subclassed (non-final) or interfaces.

Pro-Tip: Recursive type bound for Comparable:

java
// Classic pattern: "Type that can compare to itself" public static <T extends Comparable<T>> T max(T a, T b) { return a.compareTo(b) > 0 ? a : b; } // Without bound, you'd need: public static <T> T max(T a, T b) { // ❌ Cannot call compareTo() - T might not have it! }

This pattern is EVERYWHERE in Java (Collections.sort, TreeSet, etc.)

Topics Covered

Java FundamentalsAdvanced Java

Tags

#java#generics#type-safety#reusability

Last Updated

2025-02-01