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
setValueaccepts an argument of typeT, and the methodgetValuereturns a value of typeT. - In the
mainmethod, we create instances of theBoxclass 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
getArrayLengthis a generic method that works with any array typeT[]. - The type
Tis 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
printNumberhas a bounded type parameterT extends Number, meaning thatTcan only be a subtype ofNumber. - This ensures that only types like
Integer,Double, and other subclasses ofNumbercan 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
printListuses? extends Numberas the wildcard, allowing it to accept any list of typeNumberor its subclasses. - We call the method with lists of type
IntegerandDoubleas 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.