Bounded Types and Wildcards (? extends T, ? super T)
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.
Step-by-Step Guide
Step 1: Understanding Bounded Type Parameters
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 } }
Step 2: Using Wildcards with Generics
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.
Step 3: Using ? 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 } }
Step 4: Using ? 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 } }
Step 5: Combining Bounded Wildcards
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 } } }
Step 6: Best Practices for Using Wildcards and Bounded Types
-
Use
? extends T
when you need to read from a structure, but don't want to modify it. -
Use
? super T
when you need to add elements to a structure, but don't want to restrict the supertype. - Always ensure the wildcard type is compatible with the expected operations (reading or writing) on the data.
- Be cautious when combining multiple wildcard types to ensure the code remains readable and maintainable.
Conclusion
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.