![]()
Generics in Java is a feature that allows you to write classes, interfaces, and methods that operate on parameterized types. It enables type safety, code reusability, and eliminates the need for explicit type casting. Generics were introduced in Java 5 (JDK 1.5) and are widely used in the Java Collections Framework.
1. Why Use Generics?
- Type Safety: Ensures that the correct type of objects are used at compile time.
- Code Reusability: Allows writing generic algorithms that work with different types.
- Eliminates Type Casting: No need to cast objects when retrieving them from collections.
- Compile-Time Checking: Catches type-related errors at compile time rather than runtime.
2. Generic Classes
A generic class is a class that can work with any data type. It is defined using a type parameter (e.g., <T>).
Syntax:
class ClassName<T> {
// T is the type parameter
T obj;
ClassName(T obj) {
this.obj = obj;
}
T getObj() {
return obj;
}
}
Example:
class Box<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}
public class GenericsExample {
public static void main(String[] args) {
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
System.out.println("String Box: " + stringBox.getItem()); // Output: Hello
Box<Integer> integerBox = new Box<>();
integerBox.setItem(123);
System.out.println("Integer Box: " + integerBox.getItem()); // Output: 123
}
}
3. Generic Methods
A generic method is a method that can accept parameters of any type. It is defined using a type parameter before the return type.
Syntax:
<T> returnType methodName(T parameter) {
// Method body
}
Example:
public class GenericsExample {
// Generic method
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.print(element + " ");
}
System.out.println();
}
public static void main(String[] args) {
Integer[] intArray = {1, 2, 3, 4, 5};
String[] strArray = {"A", "B", "C"};
printArray(intArray); // Output: 1 2 3 4 5
printArray(strArray); // Output: A B C
}
}
4. Bounded Type Parameters
You can restrict the types that can be used as type arguments by specifying a bound (e.g., <T extends Number>).
Syntax:
<T extends UpperBoundType>
Example:
class NumericBox<T extends Number> {
private T number;
public void setNumber(T number) {
this.number = number;
}
public T getNumber() {
return number;
}
}
public class GenericsExample {
public static void main(String[] args) {
NumericBox<Integer> intBox = new NumericBox<>();
intBox.setNumber(123);
System.out.println("Integer Box: " + intBox.getNumber()); // Output: 123
// NumericBox<String> strBox = new NumericBox<>(); // Error: String is not a subclass of Number
}
}
5. Wildcards in Generics
Wildcards (?) are used to represent unknown types. They are often used in method parameters to make them more flexible.
Types of Wildcards:
- Unbounded Wildcard (
<?>):
- Represents any type.
- Example:
List<?>
- Upper Bounded Wildcard (
<? extends T>):
- Represents any type that is a subclass of
T. - Example:
List<? extends Number>
- Lower Bounded Wildcard (
<? super T>):
- Represents any type that is a superclass of
T. - Example:
List<? super Integer>
Example:
import java.util.List;
import java.util.ArrayList;
public class GenericsExample {
// Unbounded wildcard
public static void printList(List<?> list) {
for (Object element : list) {
System.out.print(element + " ");
}
System.out.println();
}
// Upper bounded wildcard
public static double sumOfList(List<? extends Number> list) {
double sum = 0.0;
for (Number number : list) {
sum += number.doubleValue();
}
return sum;
}
// Lower bounded wildcard
public static void addNumbers(List<? super Integer> list) {
list.add(1);
list.add(2);
}
public static void main(String[] args) {
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
printList(intList); // Output: 1 2
List<Double> doubleList = new ArrayList<>();
doubleList.add(1.5);
doubleList.add(2.5);
System.out.println("Sum: " + sumOfList(doubleList)); // Output: Sum: 4.0
List<Number> numList = new ArrayList<>();
addNumbers(numList);
printList(numList); // Output: 1 2
}
}
6. Type Erasure
- Generics in Java are implemented using type erasure.
- At runtime, all generic type information is erased, and the code is converted to raw types.
- For example,
List<String>becomesListat runtime.
7. Advantages of Generics
- Type Safety: Prevents
ClassCastExceptionat runtime. - Code Reusability: Write generic algorithms that work with multiple types.
- No Type Casting: Eliminates the need for explicit type casting.
- Better Readability: Makes code more readable and maintainable.
8. Example: Generic Class with Multiple Type Parameters
class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
}
public class GenericsExample {
public static void main(String[] args) {
Pair<String, Integer> pair = new Pair<>("Age", 25);
System.out.println("Key: " + pair.getKey() + ", Value: " + pair.getValue()); // Output: Key: Age, Value: 25
}
}
By using generics, you can write flexible, type-safe, and reusable code in Java!
