Generics in Java: Type-Safe and Reusable Collections


Generics in Java: Type-Safe and Reusable Collections

✅ What Are Generics in Java?

Generics allow you to define classes, interfaces, and methods with type parameters.

Without Generics (Old Style):

List list = new ArrayList();
list.add("Java");

String s = (String) list.get(0); // Need to cast manually

With Generics:

List<String> list = new ArrayList<>();
list.add("Java");

String s = list.get(0); // No casting needed

🔧 Why Use Generics?
Benefit Description
Type safety Catch errors at compile-time
No casting No need to cast when getting elements
Code reuse Write flexible, reusable code
🔹 Using Generics with Collections
List Example:

List<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);

for (int num : numbers) {
    System.out.println(num);
}

Set Example:

Set<String> names = new HashSet<>();
names.add("Alice");
names.add("Bob");

Map Example (Two type parameters):

Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 95);
scores.put("Bob", 87);

🧱 Defining Your Own Generic Class

class Box<T> {
    private T value;

    public void set(T value) {
        this.value = value;
    }

    public T get() {
        return value;
    }
}

Using Box:

Box<String> stringBox = new Box<>();
stringBox.set("Hello");
System.out.println(stringBox.get());

Box<Integer> intBox = new Box<>();
intBox.set(123);
System.out.println(intBox.get());

🧠 Common Generic Syntax
Syntax Meaning
<T> Generic type T
<K, V> Key and Value type (e.g., for Map)
<T extends Number> Upper bound – T must be a Number
<?> Wildcard – unknown type
🌀 Wildcards in Generics
Unbounded Wildcard:

List<?> list = new ArrayList<String>();

Bounded Wildcards:

// Upper bound (read-only)
List<? extends Number> numbers = new ArrayList<Integer>();

// Lower bound (write allowed)
List<? super Integer> list = new ArrayList<Number>();