Bounded Type Parameters
Restrict generic type parameters with upper bounds (extends) to access methods of the bound type.
Overview
Key Points
- •<T extends Number> means T must be Number or a subclass (Integer, Double, Long, etc.)
- •Bounded T lets you call methods of the bound type on T values
- •Multiple bounds: <T extends Comparable<T> & Cloneable>
- •Only one class bound is allowed; remaining bounds must be interfaces
- •Upper-bounded wildcards: <? extends Number> for reading from a collection
- •Lower-bounded wildcards: <? super Integer> for writing to a collection
- •Unbounded wildcard: <?> when you only use Object methods
- •The extends keyword is used for both class and interface bounds in generics
Examples
Upper-bounded type parameter
public class Main {
// T must be a Number or subclass
static <T extends Number> double sum(T[] arr) {
double total = 0;
for (T item : arr) {
total += item.doubleValue(); // can call Number methods because of the bound
}
return total;
}
static <T extends Comparable<T>> T max(T a, T b) {
return a.compareTo(b) >= 0 ? a : b;
}
public static void main(String[] args) {
Integer[] ints = {1, 2, 3, 4, 5};
Double[] doubles = {1.1, 2.2, 3.3};
System.out.println(sum(ints)); // 15.0
System.out.println(sum(doubles)); // 6.6
System.out.println(max(10, 20)); // 20
System.out.println(max("apple", "mango")); // mango
}
}Without <T extends Number>, item.doubleValue() would be a compile error because T is treated as Object. The bound gives access to Number's methods.
Upper-bounded wildcard in method parameter
import java.util.List;
public class Main {
// Accepts List<Integer>, List<Double>, List<Number>, or any List<? extends Number>
static double sumList(List<? extends Number> list) {
double total = 0;
for (Number n : list) {
total += n.doubleValue();
}
return total;
}
public static void main(String[] args) {
List<Integer> ints = List.of(1, 2, 3, 4, 5);
List<Double> doubles = List.of(1.5, 2.5, 3.0);
System.out.println(sumList(ints)); // 15.0
System.out.println(sumList(doubles)); // 7.0
}
}List<? extends Number> accepts any list whose element type is Number or a subtype. You cannot add to such a list (except null) because the actual type is unknown.
Lower-bounded wildcard
import java.util.List;
import java.util.ArrayList;
public class Main {
// Accepts List<Integer>, List<Number>, or List<Object>
static void addNumbers(List<? super Integer> list, int count) {
for (int i = 1; i <= count; i++) {
list.add(i); // safe to add Integer because the list holds Integer or a supertype
}
}
public static void main(String[] args) {
List<Number> numbers = new ArrayList<>();
addNumbers(numbers, 5);
System.out.println(numbers); // [1, 2, 3, 4, 5]
List<Object> objects = new ArrayList<>();
addNumbers(objects, 3);
System.out.println(objects); // [1, 2, 3]
}
}Producer Extends, Consumer Super (PECS): use extends when reading, super when writing. This is a standard guideline for wildcard choice.
Common Mistakes
- Using implements instead of extends for interface bounds in generics: <T extends Runnable> is correct even though Runnable is an interface
- Trying to add elements to a List<? extends T> collection. You can only read from it because the exact subtype is unknown
- Forgetting that multiple bounds require the class first: <T extends ArrayList & Comparable<T>> not <T extends Comparable<T> & ArrayList>
Note
Remember PECS: Producer Extends, Consumer Super. If a collection produces values for you to read, use <? extends T>. If a collection consumes values you write, use <? super T>.
Exercises
Write Java code and click Run to execute it instantly. When your output looks correct, mark as done.
Completed: 0 / 4 exercises
Knowledge Check
Question 1 of 4