Fundamentals 12 min read

Understanding Upper and Lower Bounds in Java Generics

This article explains Java generics' upper (extends) and lower (super) bounds, illustrating their usage with comprehensive code examples for generic classes, methods, and collections, and compares reading versus writing scenarios, helping developers write more flexible and type‑safe Java code.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Understanding Upper and Lower Bounds in Java Generics

1. Introduction

Java generics allow classes, interfaces, and methods to operate on objects of any type without specifying a concrete type. Upper and lower bounds can constrain type parameters to improve type safety and flexibility.

This article introduces the concepts of upper and lower bounds in Java generics with extensive example code.

2. Basic Concept of Generics

Generics enable definition of classes, interfaces, and methods with type parameters. Example of a generic class Box<T> that stores a value of type T.

public class Box<T> {
    private T value;
    public void setValue(T value) { this.value = value; }
    public T getValue() { return value; }
}
Box<Integer>

or Box<String> can be instantiated with specific types.

3. Upper Bound (extends)

Upper bound restricts a type parameter to a specific type or its subtypes using the extends keyword.

Example: List<? extends Number> can hold Number or any subclass such as Integer, Double.

3.1 Example: Upper bound in generic class

Class Box<T extends Number> restricts T to Number or its subclasses.

public class Box<T extends Number> {
    private T value;
    public void setValue(T value) { this.value = value; }
    public T getValue() { return value; }
}

Usage in Main demonstrates Box<Integer> and Box<Double> instances, while Box<String> is illegal.

public class Main {
    public static void main(String[] args) {
        Box<Integer> integerBox = new Box<>();
        integerBox.setValue(10);
        System.out.println("Integer Box Value: " + integerBox.getValue());

        Box<Double> doubleBox = new Box<>();
        doubleBox.setValue(10.5);
        System.out.println("Double Box Value: " + doubleBox.getValue());

        // Box<String> stringBox = new Box<>(); // compile error
    }
}

3.2 Example: Upper bound in generic method

Method sum calculates the total of a list of numbers, with type parameter bounded by Number.

public class MathUtils {
    public static <T extends Number> double sum(List<T> numbers) {
        double total = 0.0;
        for (T number : numbers) {
            total += number.doubleValue();
        }
        return total;
    }
}

Demonstration in Main shows summing integers and doubles.

public class Main {
    public static void main(String[] args) {
        List<Integer> integers = Arrays.asList(1,2,3,4,5);
        List<Double> doubles = Arrays.asList(1.1,2.2,3.3,4.4,5.5);
        System.out.println("Sum of integers: " + MathUtils.sum(integers));
        System.out.println("Sum of doubles: " + MathUtils.sum(doubles));
    }
}
Sum of integers: 15.0
Sum of doubles: 16.5

4. Lower Bound (super)

Lower bound restricts a type parameter to a specific type or its supertypes using the super keyword.

Example: List<? super Integer> can hold Integer, Number, or Object.

4.1 Example: Lower bound in generic method

Method addIntegers adds integers to a list whose element type is a supertype of Integer.

public class CollectionUtils {
    public static void addIntegers(List<? super Integer> list) {
        list.add(10);
        list.add(20);
        list.add(30);
    }
}

Usage with List<Number> and List<Object> demonstrates successful addition.

public class Main {
    public static void main(String[] args) {
        List<Number> numberList = new ArrayList<>();
        CollectionUtils.addIntegers(numberList);
        System.out.println("Number List: " + numberList);

        List<Object> objectList = new ArrayList<>();
        CollectionUtils.addIntegers(objectList);
        System.out.println("Object List: " + objectList);
    }
}
Number List: [10, 20, 30]
Object List: [10, 20, 30]

4.2 Real‑world scenario for lower bound

Lower bounds are useful for handling covariance and contravariance, such as adding Dog objects to a List<? super Dog>.

public class AnimalShelter {
    public static void addAnimals(List<? super Dog> animals) {
        animals.add(new Dog());
        // animals.add(new Cat()); // compile error
    }
}

Demonstrated with a List<Animal> where Dog instances can be safely added.

5. Comparison of Upper and Lower Bounds

Upper bounds ( extends) are typically used for reading, ensuring retrieved elements are of the bound type or its subtypes.

Lower bounds ( super) are typically used for writing, allowing addition of elements of the bound type or its subtypes.

5.1 Combining upper and lower bounds

Method copy uses both wildcards to read from a source list and write to a destination list.

public class MixedUtils {
    public static <T> void copy(List<? extends T> source, List<? super T> destination) {
        for (T item : source) {
            destination.add(item);
        }
    }
}

Example shows copying a List<Integer> into a List<Number>.

public class Main {
    public static void main(String[] args) {
        List<Integer> source = Arrays.asList(1,2,3,4,5);
        List<Number> destination = new ArrayList<>();
        MixedUtils.copy(source, destination);
        System.out.println("Destination List: " + destination);
    }
}
Destination List: [1, 2, 3, 4, 5]

6. Conclusion

The article detailed upper and lower bounds in Java generics, demonstrating how extends and super constrain type parameters, improve type safety, and enable flexible code. Proper use of bounds solves many complex type conversion and collection manipulation problems, leading to more robust and maintainable Java applications.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

GenericsType Safetylower boundUpper Bound
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.