Generics
Generics in Java allow you to write flexible and reusable code while maintaining type safety.
Instead of writing separate code for different data types, Generics let you define classes, methods, and collections that work with any type — safely.
Why Generics Are Needed
Before Generics, Java collections stored objects as Object.
This caused two major problems:
- Frequent type casting
- Runtime errors due to wrong types
Generics solve these problems by enforcing type checks at compile time.
Problem Without Generics
Consider a list without Generics.
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("Hello");
list.add(100);
String text = (String) list.get(0);
String number = (String) list.get(1); // Runtime error
}
}
This code compiles but fails at runtime. This is dangerous in real applications.
Using Generics
With Generics, you specify the data type in advance.
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
list.add("Hello");
list.add("World");
String text = list.get(0); // No casting required
System.out.println(text);
}
}
Now Java guarantees that only strings can be stored.
Generic Classes
You can create your own classes using Generics.
class Box {
T value;
void set(T value) {
this.value = value;
}
T get() {
return value;
}
}
public class Main {
public static void main(String[] args) {
Box intBox = new Box<>();
intBox.set(10);
Box strBox = new Box<>();
strBox.set("Dataplexa");
System.out.println(intBox.get());
System.out.println(strBox.get());
}
}
Generic Methods
Generics can also be used at the method level.
public class Main {
public static void printValue(T value) {
System.out.println(value);
}
public static void main(String[] args) {
printValue(100);
printValue("Java");
printValue(45.6);
}
}
The method works with any data type.
Generics with Collections
Generics are most commonly used with collections.
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map users = new HashMap<>();
users.put(1, "Admin");
users.put(2, "User");
System.out.println(users.get(1));
}
}
This ensures keys and values are always correct types.
Real-World Example
Imagine a system that stores different types of data securely.
class DataStore {
T data;
DataStore(T data) {
this.data = data;
}
T getData() {
return data;
}
}
public class Main {
public static void main(String[] args) {
DataStore name = new DataStore<>("Alice");
DataStore age = new DataStore<>(25);
System.out.println(name.getData());
System.out.println(age.getData());
}
}
Advantages of Generics
- Compile-time type safety
- No explicit casting
- Reusable and clean code
- Better readability
Key Takeaways
- Generics make Java code safer
- Errors are caught at compile time
- Widely used in collections and frameworks
- Essential for enterprise-level Java
In the next lesson, we will explore Comparators and learn how to sort objects in Java.