Introduction
Bounded type parameters in Java Generics allow you to restrict the types that can be used as type arguments in a generic class, interface, or method. This ensures that the generic type is a subtype of a specific type or implements a specific interface, providing more control and type safety in your generic code.
Table of Contents
- What are Bounded Type Parameters?
- Upper Bounded Type Parameters
- Lower Bounded Type Parameters
- Multiple Bounds
- Example Programs
- Conclusion
1. What are Bounded Type Parameters?
Bounded type parameters restrict the types that can be used as type arguments. There are two types of bounds in Java Generics:
- Upper Bound: Specifies that the type parameter must be a subtype of a specific type.
- Lower Bound: Specifies that the type parameter must be a supertype of a specific type.
2. Upper Bounded Type Parameters
Upper bounded type parameters restrict the type parameter to be a specific type or its subclasses. The extends
keyword is used to define an upper bound.
Syntax:
class ClassName<T extends SuperClass> {
// Class body
}
Example:
public class UpperBoundedExample {
// Generic method with upper bounded type parameter
public static <T extends Number> void printDoubleValue(T value) {
System.out.println(value.doubleValue());
}
public static void main(String[] args) {
printDoubleValue(10); // Integer
printDoubleValue(3.14); // Double
printDoubleValue(5.67f); // Float
}
}
Output:
10.0
3.14
5.670000076293945
Explanation:
- The
printDoubleValue
method accepts any type that extendsNumber
. - It prints the double value of the passed argument.
3. Lower Bounded Type Parameters
Lower bounded type parameters restrict the type parameter to be a specific type or its supertypes. The super
keyword is used to define a lower bound.
Syntax:
class ClassName<T super SubClass> {
// Class body
}
Example:
import java.util.List;
import java.util.ArrayList;
public class LowerBoundedExample {
// Generic method with lower bounded type parameter
public static void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 5; i++) {
list.add(i);
}
}
public static void main(String[] args) {
List<Number> numberList = new ArrayList<>();
addNumbers(numberList);
System.out.println("Number List: " + numberList);
}
}
Output:
Number List: [1, 2, 3, 4, 5]
Explanation:
- The
addNumbers
method accepts a list that can holdInteger
objects or any of its supertypes (Number
in this case). - It adds integers 1 to 5 to the list.
4. Multiple Bounds
A type parameter can have multiple bounds. A type parameter with multiple bounds must follow this syntax: <T extends A & B & C>
. Note that a class must come first, followed by interfaces.
Syntax:
class ClassName<T extends ClassA & InterfaceB & InterfaceC> {
// Class body
}
Example:
interface Printable {
void print();
}
class Document {
public void show() {
System.out.println("Showing document");
}
}
class Report extends Document implements Printable {
@Override
public void print() {
System.out.println("Printing report");
}
}
public class MultipleBoundsExample {
// Generic method with multiple bounds
public static <T extends Document & Printable> void process(T item) {
item.show();
item.print();
}
public static void main(String[] args) {
Report report = new Report();
process(report);
}
}
Output:
Showing document
Printing report
Explanation:
- The
process
method accepts any type that extendsDocument
and implementsPrintable
. - It calls the
show
andprint
methods on the passed argument.
5. Example Programs
Example 1: Upper Bounded Type Parameter in Generic Class
Example:
class Box<T extends Number> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public double getDoubleValue() {
return value.doubleValue();
}
}
public class UpperBoundedGenericClassExample {
public static void main(String[] args) {
Box<Integer> intBox = new Box<>();
intBox.setValue(100);
System.out.println("Integer Value: " + intBox.getValue());
System.out.println("Double Value: " + intBox.getDoubleValue());
Box<Double> doubleBox = new Box<>();
doubleBox.setValue(123.45);
System.out.println("Double Value: " + doubleBox.getValue());
System.out.println("Double Value: " + doubleBox.getDoubleValue());
}
}
Output:
Integer Value: 100
Double Value: 100.0
Double Value: 123.45
Double Value: 123.45
Example 2: Lower Bounded Type Parameter in Method
Example:
import java.util.List;
import java.util.ArrayList;
public class LowerBoundedGenericMethodExample {
public static void addValues(List<? super Number> list) {
list.add(1);
list.add(2.5);
list.add(3.14f);
}
public static void main(String[] args) {
List<Object> objList = new ArrayList<>();
addValues(objList);
System.out.println("Object List: " + objList);
List<Number> numberList = new ArrayList<>();
addValues(numberList);
System.out.println("Number List: " + numberList);
}
}
Output:
Object List: [1, 2.5, 3.14]
Number List: [1, 2.5, 3.14]
6. Conclusion
Bounded type parameters in Java Generics provide greater control over the types used as arguments in generic classes, methods, and interfaces. They ensure type safety and allow you to define constraints on the type parameters. Upper bounds restrict the type to a specific class or its subclasses, while lower bounds restrict the type to a specific class or its supertypes. Multiple bounds allow a type parameter to extend multiple classes and interfaces, adding further flexibility.
By understanding and using bounded type parameters, you can create more robust and flexible generic code in Java.
Happy coding!
Comments
Post a Comment
Leave Comment