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 NumberMultiple 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 Type | Syntax | Use 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.)