0% found this document useful (0 votes)
16 views8 pages

Java Generics2

The document discusses the implementation of generics in Java, highlighting their importance for type safety and code reusability. It covers various aspects such as generic classes, multiple type parameters, bounded type parameters, wildcards, and the implications of type erasure at runtime. Additionally, it outlines common pitfalls, design patterns, and best practices for effectively using generics in Java programming.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
16 views8 pages

Java Generics2

The document discusses the implementation of generics in Java, highlighting their importance for type safety and code reusability. It covers various aspects such as generic classes, multiple type parameters, bounded type parameters, wildcards, and the implications of type erasure at runtime. Additionally, it outlines common pitfalls, design patterns, and best practices for effectively using generics in Java programming.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 8

Implementing Generics in Java

Robust Typing, Bounded Polymorphism, and Erasure Semantics for Clean, Reusable Code

🚀 Why Generics Matter


Java generics provide a compile-time mechanism to enforce type safety without sacrificing
flexibility. By introducing type parameters, developers can build:

Strongly-typed, reusable data structures


Declarative algorithms (e.g. Collections.sort(List<T>) )
API contracts that are self-documenting and fail early

1/8
Behind the scenes, generics are a syntactic abstraction. Under type erasure, they vanish at
runtime — making understanding their limitations as important as their capabilities.

🔧 1. Generic Classes – Syntax & Structure


Generic classes are declared using angle brackets <> :

public class MyList<T> {


private T[] items;
private int index = 0;

@SuppressWarnings("unchecked")
public MyList(int size) {
items = (T[]) new Object[size]; // type erasure workaround
}

public void add(T item) {


items[index++] = item;
}

public T get(int i) {
return items[i];
}
}

⚠️Key Pitfalls
You cannot instantiate generic arrays directly: new T[10] → ❌
You must explicitly cast when initializing: (T[]) new Object[size]
Without @SuppressWarnings("unchecked") , you'll get compiler warnings due to unsafe
casting

🧭 2. Multiple Type Parameters


Generics can declare multiple type parameters. Each must be distinct and follow type
naming conventions.

public class Pair<K, V> {


private final K key;
private final V value;

public Pair(K key, V value) {


this.key = key;
this.value = value;
}

2/8
public K getKey() { return key; }
public V getValue() { return value; }
}

📌 Use Cases
Pair<K, V> → for mapping keys to values
Map<K, V> → for typed key-value stores
Function<T, R> → for declaring input/output processors

⛓ 3. Bounded Type Parameters


Bounded type parameters limit acceptable types and enable richer semantics.

3.1 Single Bound

public class Calculator<T extends Number> {


public double doubleValue(T input) {
return input.doubleValue();
}
}

3.2 Multiple Bounds

public class ComparableBox<T extends Number & Comparable<T>> {


public boolean isGreaterThan(T a, T b) {
return a.compareTo(b) > 0;
}
}

💡 Rule
You can only extend one class ( Number here), but implement multiple interfaces.

🔄 4. Generic Methods
public class Utils {
public static <T> T getFirst(List<T> list) {
return list.isEmpty() ? null : list.get(0);
}
}

3/8
🧠 Notes
The <T> must be declared before the return type
Class generics ≠ method generics: they are scoped independently
Static methods must declare their own type parameters

🌀 5. Wildcards: ? , extends , super


Wildcards allow covariance and contravariance in method arguments.

Concept Relationship to Subtyping Direction of Allowed Type Conversion


Covariance Preserves subtyping (read- Subtype → Supertype allowed (read
only) from)
Contravariance Reverses subtyping (write- Supertype → Subtype allowed (write
only) to)
Invariance No subtyping relation Must be exactly the same type

✅ Covariance – “Producer Extends”


Covariance allows a generic type to accept subtypes of a given type. You read from but
cannot write to it safely.

List<? extends Number> list = new ArrayList<Integer>();


Number n = list.get(0); // ✅ OK
list.add(1); // ❌ Compile error

🧠 Think: "I can treat a


List<Integer> as a List<? extends Number> because I’m only
consuming/reading Numbers."

🔁 Contravariance – “Consumer Super”


Contravariance allows a generic type to accept supertypes. You can write to it, but reading is
only safe as Object .

List<? super Integer> list = new ArrayList<Number>();


list.add(1); // ✅ OK
Object o = list.get(0); // ✅ Only as Object
Integer i = list.get(0); // ❌ Compile error

🧠 Think: "I can add


Integer s to List<? super Integer> — which may be
List<Integer> or List<Number> — but I don't know what I’ll get out."

4/8
❗️ Invariance – Default in Java Generics
Java generics are invariant by default:

List<Object> objects;
List<String> strings = new ArrayList<>();

objects = strings; // ❌ Compile error

Even though String extends Object , List<String> is not a subtype of List<Object> .

🤯 Real-world Analogy
Covariant: You can borrow a book from a Library of Books (read-only) even if it's
specifically a Library of Novels.
Contravariant: You can donate any novel to a Library accepting Books (write-only).

📌 In Java: ? extends vs ? super

Usage Behavior Example


List<? extends T> Covariant (read) Accepts any list of T or its subtypes
List<? super T> Contravariant (write) Accepts any list of T or its supertypes

🔄 PECS Rule (by Joshua Bloch)


"Producer Extends, Consumer Super"

If a parameterized type produces values, use ? extends T


If it consumes values, use ? super T

5.1 Unbounded Wildcard

public void printAll(List<?> list) {


for (Object obj : list) {
System.out.println(obj);
}
}

5.2 Upper Bound – <? extends T> (Covariant)

public double sum(List<? extends Number> list) {


return list.stream().mapToDouble(Number::doubleValue).sum();

5/8
}

→ Can read safely, but cannot write.

5.3 Lower Bound – <? super T> (Contravariant)

public void fillWithIntegers(List<? super Integer> list) {


list.add(42);
}

→ Can write safely, but cannot assume readable type beyond Object .

🧮 6. Type Erasure: Runtime Limitations


Java compiles generics into raw types and erases the parameter types at runtime.

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


List<Integer> ints = new ArrayList<>();

System.out.println(list.getClass() == ints.getClass()); // true

Impacts:

Limitation Explanation
No instanceof for generics obj instanceof List<String> → ❌ compile error
No reflection of generic list.getClass().getTypeParameters() → metadata
types only
No generic array creation new T[10] is illegal → use casting workaround

📉 7. Common Pitfalls & Anti-patterns


Pitfall Example Fix
Using raw types List list = new Always parameterize:
ArrayList(); List<String>
Overusing wildcards List<? extends Prefer List<T> when defining
Object> APIs
Type inference surprises <T> T cast(Object Enforce type with <T extends
obj) Foo>
Ineffective bounds T extends Object Redundant — all classes extend
Object
6/8
Pitfall Example Fix
Exposing wildcards in public List<?> Prefer List<T> or use bounded
return types getData() wildcards in args

🔐 8. Generic Interfaces
public interface Converter<S, T> {
T convert(S source);
}

Use in APIs

public class StringToInteger implements Converter<String, Integer> {


public Integer convert(String input) {
return Integer.valueOf(input);
}
}

🏗 9. Design Patterns Using Generics


Pattern Application
Factory Pattern public <T> T create(Class<T> clazz)
Fluent Builder return (T) this with bounded self-referencing generics ( T
extends Builder<T> )
Strategy Pattern interface Validator<T> – replace conditionals with polymorphism
Decorator Processor<T> wrappers that extend behavior via composition
Pattern

✅ 10. Best Practices


✅ Name type params meaningfully: T , E , K , V , R
✅ Minimize wildcard exposure in public APIs
✅ Prefer composition of Function<T, R> over ad hoc custom interfaces
❌ Avoid raw types — type safety is lost
✅ Use Optional<T> instead of nullable generic return types
✅ Use bounded generics ( T extends Comparable<T> ) to enable rich behavior

🧾 11. Summary Table


7/8
Concept Description
Generic Class Parameterized data container ( class Box<T> )
Generic Method Independent type logic ( <T> T identity(T input) )
Bounded Types Constrain T with extends , multiple bounds ( & )
Wildcards ? , ? extends , ? super for flexible API contracts
Type Erasure Type information erased at runtime → no casts, instanceof, arrays
Invariance List<A> ≠ List<B> even if A extends B
Patterns Factory, Builder, Strategy, Adapter with generic parametrization
Reflection Generic type info only partially available via TypeVariable

8/8

You might also like