⚡IntermediateGenerics

Bounded Type Parameters

Restrict generic type parameters with upper bounds (extends) to access methods of the bound type.

Overview

Bounded type parameters restrict which types can be used as type arguments. An upper bound (<T extends SomeClass>) means T must be SomeClass or a subclass of it. This lets you call methods defined in SomeClass on the type parameter. Without a bound, T is treated as Object, and you can only call Object methods. With <T extends Number>, you can call intValue(), doubleValue(), and other Number methods on T. Multiple bounds are written as <T extends ClassA & InterfaceB & InterfaceC>. Only one class is allowed (and it must come first); the rest must be interfaces.

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.

Loading editor…

Completed: 0 / 4 exercises

Knowledge Check

Question 1 of 4

What does <T extends Comparable<T>> mean?