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.
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 that stores a value of type T.
public class Box
{
private T value;
public void setValue(T value) { this.value = value; }
public T getValue() { return value; }
}Box or Box 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 can hold Number or any subclass such as Integer , Double .
3.1 Example: Upper bound in generic class
Class Box restricts T to Number or its subclasses.
public class Box
{
private T value;
public void setValue(T value) { this.value = value; }
public T getValue() { return value; }
}Usage in Main demonstrates Box and Box instances, while Box is illegal.
public class Main {
public static void main(String[] args) {
Box
integerBox = new Box<>();
integerBox.setValue(10);
System.out.println("Integer Box Value: " + integerBox.getValue());
Box
doubleBox = new Box<>();
doubleBox.setValue(10.5);
System.out.println("Double Box Value: " + doubleBox.getValue());
// Box
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
double sum(List
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
integers = Arrays.asList(1,2,3,4,5);
List
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.54. Lower Bound (super)
Lower bound restricts a type parameter to a specific type or its supertypes using the super keyword.
Example: List 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
list) {
list.add(10);
list.add(20);
list.add(30);
}
}Usage with List and List demonstrates successful addition.
public class Main {
public static void main(String[] args) {
List
numberList = new ArrayList<>();
CollectionUtils.addIntegers(numberList);
System.out.println("Number List: " + numberList);
List
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 .
public class AnimalShelter {
public static void addAnimals(List
animals) {
animals.add(new Dog());
// animals.add(new Cat()); // compile error
}
}Demonstrated with a List 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
void copy(List
source, List
destination) {
for (T item : source) {
destination.add(item);
}
}
}Example shows copying a List into a List .
public class Main {
public static void main(String[] args) {
List
source = Arrays.asList(1,2,3,4,5);
List
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.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.