Generics
Generics make code safer and more flexible. This lesson covers Java generics usage.
What are Generics
Generics are parameterized types that allow classes and methods to work with multiple types.
Benefits of Generics
| Benefit | Description |
|---|---|
| Type safety | Compile-time type checking |
| Code reuse | One code works with multiple types |
| Eliminate casting | No manual type conversion needed |
Generic Classes
Syntax
JAVA
public class ClassName<T> {
// Use T as type
}
Example: Generic Class
JAVA
public class Box<T> {
private T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
public static void main(String[] args) {
// Store Integer
Box<Integer> intBox = new Box<>();
intBox.set(100);
int num = intBox.get(); // No casting needed
// Store String
Box<String> strBox = new Box<>();
strBox.set("Hello");
String str = strBox.get();
System.out.println("Integer Box: " + num);
System.out.println("String Box: " + str);
}
}
Multiple Generic Parameters
JAVA
public 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; }
@Override
public String toString() {
return key + "=" + value;
}
public static void main(String[] args) {
Pair<String, Integer> pair = new Pair<>("Alice", 95);
System.out.println(pair); // Alice=95
}
}
Generic Methods
Syntax
JAVA
public static <T> methodName(parameters) {
// Use T
}
Example: Generic Methods
JAVA
public class GenericMethod {
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.print(element + " ");
}
System.out.println();
}
public static <T> T getFirst(T[] array) {
if (array == null || array.length == 0) {
return null;
}
return array[0];
}
public static <T extends Comparable<T>> T getMax(T[] array) {
T max = array[0];
for (T element : array) {
if (element.compareTo(max) > 0) {
max = element;
}
}
return max;
}
public static void main(String[] args) {
Integer[] nums = {1, 2, 3, 4, 5};
String[] names = {"Alice", "Bob", "Charlie"};
printArray(nums); // 1 2 3 4 5
printArray(names); // Alice Bob Charlie
System.out.println("First number: " + getFirst(nums)); // 1
System.out.println("First name: " + getFirst(names)); // Alice
System.out.println("Max: " + getMax(nums)); // 5
System.out.println("Max name: " + getMax(names)); // Charlie
}
}
Generic Interfaces
Defining Generic Interface
JAVA
public interface Repository<T> {
void add(T item);
T get(int index);
List<T> getAll();
}
Implementing Generic Interface
JAVA
public class ArrayListRepository<T> implements Repository<T> {
private List<T> list = new ArrayList<>();
@Override
public void add(T item) {
list.add(item);
}
@Override
public T get(int index) {
return list.get(index);
}
@Override
public List<T> getAll() {
return new ArrayList<>(list);
}
public static void main(String[] args) {
Repository<String> repo = new ArrayListRepository<>();
repo.add("Alice");
repo.add("Bob");
System.out.println(repo.getAll()); // [Alice, Bob]
}
}
Generic Wildcards
Unbounded Wildcard ?
JAVA
public static void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}
Upper Bounded Wildcard ? extends T
JAVA
// Only accepts Number and its subclasses
public static double sum(List<? extends Number> list) {
double total = 0;
for (Number num : list) {
total += num.doubleValue();
}
return total;
}
Lower Bounded Wildcard ? super T
JAVA
// Only accepts Integer and its superclasses
public static void addNumbers(List<? super Integer> list) {
list.add(1);
list.add(2);
list.add(3);
}
Example: Wildcards
JAVA
import java.util.ArrayList;
import java.util.List;
public class WildcardDemo {
// Unbounded wildcard
public static void printList(List<?> list) {
for (Object item : list) {
System.out.print(item + " ");
}
System.out.println();
}
// Upper bounded wildcard
public static double sum(List<? extends Number> list) {
double total = 0;
for (Number num : list) {
total += num.doubleValue();
}
return total;
}
public static void main(String[] args) {
List<Integer> ints = new ArrayList<>();
ints.add(1);
ints.add(2);
ints.add(3);
List<String> strs = new ArrayList<>();
strs.add("Alice");
strs.add("Bob");
printList(ints); // 1 2 3
printList(strs); // Alice Bob
System.out.println("sum: " + sum(ints)); // 6.0
}
}
Type Erasure
Java generics are implemented through type erasure—generic information is erased after compilation.
JAVA
// Before compilation
List<String> list = new ArrayList<>();
list.add("Hello");
// After compilation (type erasure)
List list = new ArrayList();
list.add("Hello");
String str = (String) list.get(0); // Automatic casting
⚠️ Note: Generic type information cannot be obtained at runtime.
List<String> and List<Integer> are the same type at runtime.
Generic Constraints
Example: Bounded Generics
JAVA
// T must implement Comparable
public static <T extends Comparable<T>> T min(T a, T b) {
return a.compareTo(b) <= 0 ? a : b;
}
// T must be a subclass of Number
public static <T extends Number> double doubleValue(T num) {
return num.doubleValue();
}
Common Generic Names
| Name | Meaning |
|---|---|
T |
Type |
E |
Element |
K |
Key |
V |
Value |
N |
Number |
❓ Frequently Asked Questions
Q Why use generics?
A Type safety, code reuse, and eliminating casting.
Q Do generics support primitive types?
A No, use wrapper classes. For example,
List<int> should be List<Integer>.Q What's the difference between
<?> and <Object>?A
<?> is unknown type—can only read, not write. <Object> is Object type—can read and write.📖 Summary
- Generics make code safer and more flexible
- Generic classes, generic methods, generic interfaces
- Wildcards:
?,? extends,? super - Type erasure: generic information is erased after compilation
📝 Exercises
- Generic container: Implement a generic Stack class
- Generic method: Implement a generic method to convert an array to a List
- Bounded generics: Implement a method to compare two objects
Next Lesson
In the next lesson, we'll learn about Practice: Collection Applications — applying collection knowledge.



