Java generics provide compile-time type safety by allowing you to define classes, interfaces, and methods with type parameters. These parameters can later be replaced with specific types when the generic is used.
Type Bounds
Type bounds restrict the kinds of types that can be passed to a generic type parameter. Without bounds, a type parameter can represent any object type (i.e., Object
is the implicit upper bound). However, you can explicitly specify a bounded type parameter using the extends
keyword.
Syntax
<T extends ClassName>
You can restrict to a class (e.g., Number)
Or to an interface (e.g., Comparable<T>)
Or a combination (e.g., T extends Number & Comparable<T>)
Code language: HTML, XML (xml)
This ensures that the type used must be a subclass (or implementer) of the specified class or interface.
Purpose:
-
To call methods of the bounded class/interface.
-
To ensure logical constraints in your code.
-
To allow polymorphic behavior within generics.
Multiple Type Parameters
Generics in Java are not limited to a single type parameter. You can define multiple type parameters by separating them with commas.
Syntax:
class ClassName<T, U, V> { ... }
Code language: JavaScript (javascript)
Each type parameter acts independently unless bounds are applied. This allows different types to interact within a single class or method.
Use Cases:
-
Representing key-value pairs (like in maps)
-
Handling related objects of different types
-
Designing generic data structures or utility classes
Combining Type Bounds with Multiple Type Parameters
You can also use type bounds alongside multiple type parameters. This allows for precise control over the types that can be passed and the operations that can be performed on them.
Syntax:
<T extends Comparable<T>, U>
This defines one bounded parameter (T) and another generic (U).
Practical Application: Sorting a Pair of Comparable Items
Let’s consider a generic utility that takes two objects of a comparable type and returns them in sorted order. This is useful in scenarios such as building comparison utilities, report generators, or data normalization tools.
// Utility class with type bounds and multiple parameters class Sorter<T extends Comparable<T>, U> { private T first; private T second; private U label; public Sorter(T first, T second, U label) { this.first = first; this.second = second; this.label = label; } public void sortAndPrint() { if (first.compareTo(second) > 0) { System.out.println(label + ": " + second + ", " + first); } else { System.out.println(label + ": " + first + ", " + second); } } } // Main class to demonstrate practical use public class Main { public static void main(String[] args) { Sorter<Integer, String> intSorter = new Sorter<>(20, 10, "Sorted Numbers"); intSorter.sortAndPrint(); // Output: Sorted Numbers: 10, 20 Sorter<String, String> nameSorter = new Sorter<>("Paani", "Mahesh", "Names"); nameSorter.sortAndPrint(); // Output: Names: Mahesh, Paani } } /* Sorted Numbers: 10, 20 Names: Mahesh, Paani */
Type bounds and multiple type parameters are powerful features of Java generics that enhance type safety, code reusability, and flexibility in generic programming.