Bounded types and wildcards in Java generics are powerful concepts that enable more flexible and reusable code.
They provide fine-grained control over type parameters in generic classes, methods, and interfaces. In this article,
we will explore the concepts of bounded types and wildcards (? extends T
, ? super T
) and provide examples to demonstrate their usage in advanced Java.
In Java, you can restrict the types that can be used as type parameters. This restriction is called a bounded type parameter.
Bounded types are specified using the extends
keyword for upper bounds, meaning the type parameter must be a subclass of a given type.
Example: Bounded type parameter with an upper bound
// Bounded type parameter example class Box<T extends Number> { private T value; public void setValue(T value) { this.value = value; } public T getValue() { return value; } } public class BoundedTypeExample { public static void main(String[] args) { Box<Integer> integerBox = new Box<>(); integerBox.setValue(10); System.out.println("Integer Value: " + integerBox.getValue()); Box<Double> doubleBox = new Box<>(); doubleBox.setValue(10.5); System.out.println("Double Value: " + doubleBox.getValue()); // The following will cause a compile-time error // Box<String> stringBox = new Box<>(); // Error: String is not a subclass of Number } }
Wildcards in Java generics are used to represent an unknown type. The most common wildcard types are ? extends T
(upper bounded wildcard) and ? super T
(lower bounded wildcard). These wildcards allow you to work with
generics without knowing the exact type.
? extends T
(Upper Bounded Wildcard)
The ? extends T
wildcard is used when you want to allow a method or class to accept types that are either
of type T
or a subtype of T
. This is useful when you want to read data from a structure without modifying it.
Example: Using ? extends T
to read elements of a list
import java.util.*; public class UpperBoundedWildcardExample { // Method that accepts a list of elements that are instances of Number or its subclasses public static void printNumbers(List<? extends Number> list) { for (Number num : list) { System.out.println(num); } } public static void main(String[] args) { List<Integer> intList = Arrays.asList(1, 2, 3, 4); List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3); printNumbers(intList); // Valid: Integer is a subclass of Number printNumbers(doubleList); // Valid: Double is a subclass of Number } }
? super T
(Lower Bounded Wildcard)
The ? super T
wildcard is used when you want to allow a method or class to accept types that are either
of type T
or a supertype of T
. This is useful when you want to add elements to a structure
but don't know the exact type, as it guarantees that the structure can accept T
or any superclass.
Example: Using ? super T
to add elements to a list
import java.util.*; public class LowerBoundedWildcardExample { // Method that accepts a list of elements that are instances of Number or its superclasses public static void addNumbers(List<? super Integer> list) { list.add(10); // Valid: Integer is a subclass of Number list.add(20); // Valid: Integer is a subclass of Number } public static void main(String[] args) { List<Number> numberList = new ArrayList<>(); addNumbers(numberList); // Valid: Number is a superclass of Integer List<Object> objectList = new ArrayList<>(); addNumbers(objectList); // Valid: Object is a superclass of Integer // The following will cause a compile-time error // List<Double> doubleList = new ArrayList<>(); // addNumbers(doubleList); // Error: Double is not a superclass of Integer } }
You can also combine ? extends T
and ? super T
in more complex scenarios. For example,
you might have a method that reads from one list (using ? extends T
) and adds to another (using ? super T
).
Example: Combining ? extends T
and ? super T
import java.util.*; public class CombinedWildcardExample { // Method to copy elements from one list to another public static void copyList(List<? extends Number> source, List<? super Number> destination) { for (Number num : source) { destination.add(num); // Add elements to the destination list } } public static void main(String[] args) { List<Integer> intList = Arrays.asList(1, 2, 3); List<Number> numberList = new ArrayList<>(); copyList(intList, numberList); // Valid: Integer is a subclass of Number for (Number num : numberList) { System.out.println(num); // Output: 1, 2, 3 } } }
? extends T
when you need to read from a structure, but don't want to modify it.? super T
when you need to add elements to a structure, but don't want to restrict the supertype.
Bounded types and wildcards (? extends T
, ? super T
) in Java generics provide a way to write flexible, reusable,
and type-safe code. By understanding and using these concepts, you can write methods and classes that work with a wide variety of
types while ensuring type safety. Wildcards and bounded types are crucial for writing generic code that is both efficient and maintainable.