Generics in Java
Introduction
Generics are a powerful feature in Java that allow you to write more flexible and reusable code while maintaining type safety. Generics enable classes, interfaces, and methods to operate on objects of various types while ensuring that errors are caught during compile time rather than runtime.
In this tutorial, we will explore the basics of generics in Java, including generic classes, methods, and bounded types, and show how they can help make your code more robust and flexible.
Step 1: Generic Classes
A generic class allows you to define a class that can operate on any type of object. You define a class with a type parameter, and the type is specified when you create an instance of the class.
// Define a generic class public class Box<T> { private T value; public void setValue(T value) { this.value = value; } public T getValue() { return value; } } public class Main { public static void main(String[] args) { // Create a Box for storing Integer Box<Integer> integerBox = new Box<>(); integerBox.setValue(10); System.out.println("Integer Value: " + integerBox.getValue()); // Create a Box for storing String Box<String> stringBox = new Box<>(); stringBox.setValue("Hello Generics"); System.out.println("String Value: " + stringBox.getValue()); } }
In this example:
- The class
Box<T>
is a generic class with a type parameterT
. - The method
setValue
accepts an argument of typeT
, and the methodgetValue
returns a value of typeT
. - In the
main
method, we create instances of theBox
class for different types (Integer and String) and use them accordingly.
Step 2: Generic Methods
You can also create generic methods that work with different types. The type parameter is specified before the return type of the method, allowing you to apply the method to any type.
public class Main { // Generic method that returns the length of any type of array public static <T> int getArrayLength(T[] array) { return array.length; } public static void main(String[] args) { Integer[] intArray = {1, 2, 3, 4}; String[] strArray = {"Hello", "Generics"}; // Call the generic method with Integer array System.out.println("Integer Array Length: " + getArrayLength(intArray)); // Call the generic method with String array System.out.println("String Array Length: " + getArrayLength(strArray)); } }
In this example:
- The method
getArrayLength
is a generic method that works with any array typeT[]
. - The type
T
is specified when calling the method, and the method returns the length of the array.
Step 3: Bounded Type Parameters
Java generics allow you to specify bounds for the type parameters. This means you can restrict the types that are allowed to be passed as arguments to a generic class or method.
// Define a generic method with bounded type public class Main { public static <T extends Number> void printNumber(T number) { System.out.println("Number: " + number); } public static void main(String[] args) { // Call the generic method with different types of Number printNumber(10); // Integer printNumber(10.5); // Double } }
In this example:
- The method
printNumber
has a bounded type parameterT extends Number
, meaning thatT
can only be a subtype ofNumber
. - This ensures that only types like
Integer
,Double
, and other subclasses ofNumber
can be passed to the method.
Step 4: Wildcards in Generics
Wildcards are used to represent an unknown type in a generic class or method. There are three types of wildcards in Java generics: ?
, ? extends T
, and ? super T
.
The wildcard ?
represents any type, ? extends T
is used for an upper-bounded wildcard (restricting to subtypes of T
), and ? super T
is used for a lower-bounded wildcard (restricting to superclasses of T
).
public class Main { // Method using wildcard public static void printList(java.util.List<? extends Number> list) { for (Number num : list) { System.out.println(num); } } public static void main(String[] args) { java.util.List<Integer> intList = java.util.Arrays.asList(1, 2, 3); java.util.List<Double> doubleList = java.util.Arrays.asList(1.1, 2.2, 3.3); // Call the method with different types of lists printList(intList); printList(doubleList); } }
In this example:
- The method
printList
uses? extends Number
as the wildcard, allowing it to accept any list of typeNumber
or its subclasses. - We call the method with lists of type
Integer
andDouble
as arguments.
Conclusion
In this tutorial, we learned the following about generics in Java:
- We defined and used generic classes that can work with any type.
- We created generic methods that accept and return different types.
- We explored bounded type parameters to restrict the types that can be used with generics.
- We learned how to use wildcards to represent unknown types in generic classes and methods.