Generics — Type Parameters & Wildcards
জেনেরিকস — Type Parameter ও Wildcard
1. Why Generics Exist
Before Java 5, a List held plain Objects. You could put a String
in and later, by mistake, an Integer — the compiler would not stop you, and your code would
blow up at runtime with a ClassCastException. Generics fixed this by letting
you parameterise a class or method by a type, so the compiler enforces consistency and you can drop the
casts.
List-এ শুধু Object রাখা যেত — String-এর পাশে ভুল করে Integer রাখলে compile-এ ধরা পড়ত না, runtime-এ ClassCastException। Generics class/method-কে type দিয়ে parameterise করতে দেয়, তাই compiler-ই mismatch ধরে ফেলে এবং cast-এর প্রয়োজন থাকে না।
import java.util.*;
class Main {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Rahim");
// names.add(42); // ❌ compile error — compiler protects us
String first = names.get(0); // no cast needed
System.out.println(first);
}
}
2. A Generic Class of Your Own
class Box<T> {
private T value;
Box(T value) { this.value = value; }
public T get() { return value; }
public void set(T v) { this.value = v; }
}
class Main {
public static void main(String[] args) {
Box<String> s = new Box<>("hello");
Box<Integer> n = new Box<>(42);
System.out.println(s.get().length()); // String method, safe
System.out.println(n.get() + 1); // Integer auto-unbox
}
}
<T> একটি type parameter — class বানানোর সময় একটি placeholder, instantiation-এ (Box<String>) কংক্রিট type বসে। Convention: T, E (element), K/V (key/value), R (result)।
3. Bounded Type Parameters
Sometimes you want to restrict a type parameter. <T extends Number> means "any
T that is a subtype of Number" — inside the method you can safely call
Number's methods on it.
import java.util.*;
class Main {
static <T extends Number> double sum(List<T> xs) {
double total = 0;
for (T x : xs) total += x.doubleValue();
return total;
}
public static void main(String[] args) {
System.out.println(sum(List.of(1, 2, 3)));
System.out.println(sum(List.of(1.5, 2.5)));
}
}
4. Wildcards and the PECS Rule
Generics in Java are invariant: a List<Integer> is NOT a
List<Number>, even though Integer is a Number. Wildcards
restore the flexibility you expect while preserving safety:
? extends T— "some unknown subtype ofT"; safe to read asT, unsafe to write. Producer.? super T— "some unknown supertype ofT"; safe to write aT, unsafe to read specifically. Consumer.
? extends T. If it consumes T (you write to it) use
? super T.
PECS — Producer হলে extends, Consumer হলে super। থেকে পড়লে extends, তাতে লিখলে super।
import java.util.*;
class Main {
// src PRODUCES Numbers (we read) → ? extends Number
// dst CONSUMES Numbers (we write) → ? super Number
static void copyNumbers(List<? extends Number> src,
List<? super Number> dst) {
for (Number n : src) dst.add(n);
}
public static void main(String[] args) {
List<Integer> ints = List.of(1, 2, 3);
List<Object> target = new ArrayList<>();
copyNumbers(ints, target);
System.out.println(target);
}
}
5. Type Erasure — The Honest Reality
Java generics are a compile-time feature. At runtime, all parametric type information is erased:
a List<String> and a List<Integer> both become plain
List. This kept Java's generics compatible with pre-Java-5 code, but it leads to a few quirks:
- You cannot do
new T[10]— the JVM does not know T at runtime. instanceof List<String>is illegal — onlyinstanceof List<?>is allowed.- Two overloads that differ only in their generic parameter are not allowed — they erase to the same signature.
new T[], instanceof List<String>-এর মতো সীমাবদ্ধতাও এনেছে।
6. Vocabulary (শব্দভাণ্ডার)
| Term | Meaning | বাংলায় |
|---|---|---|
| Type parameter | Placeholder like T filled in when instantiating. | Placeholder — instantiation-এ কংক্রিট type বসে। |
| Generic class | A class parameterised by one or more types. | Type-parameterised class। |
| Bounded type | <T extends Foo> — T must be Foo or a subtype. | T অবশ্যই Foo বা তার subtype। |
| Wildcard | ? — unknown type. | অজানা type। |
Upper bounded ? extends T | Unknown subtype of T — for reading. | T-এর subtype — পড়ার জন্য। |
Lower bounded ? super T | Unknown supertype of T — for writing. | T-এর supertype — লেখার জন্য। |
| PECS | Producer Extends, Consumer Super. | Producer হলে extends, Consumer হলে super। |
| Type erasure | Generic info stripped at runtime. | Runtime-এ type তথ্য মুছে যায়। |
7. Practice Problems
-
Write a generic class
Pair<A, B>withfirst()andsecond()accessors. Build aPair<String, Integer>and print both values.Pair<A, B>generic class বানান (first(),second())।Pair<String, Integer>তৈরি করে দেখান।✨ Show Answer
Main.javaclass Pair<A, B> { private final A a; private final B b; Pair(A a, B b) { this.a = a; this.b = b; } A first() { return a; } B second() { return b; } } class Main { public static void main(String[] args) { Pair<String, Integer> p = new Pair<>("Age", 28); System.out.println(p.first() + "=" + p.second()); } } -
Explain in 2–3 sentences why
List<Integer>is not aList<Number>even thoughIntegeris aNumber.দুই-তিন বাক্যে —IntegerNumberহলেওList<Integer>কেনList<Number>নয়?✨ Show Answer
Answer: If it were, you could pass a
List<Integer>where aList<Number>is expected and thenadd(3.14)to it — silently breaking the original Integer list's invariants. Java therefore keeps generic types invariant, and offers wildcards (? extends Number) when you need flexibility in a read-only way.যদি এটা হতো,
List<Integer>-এ3.14ঢুকে যেতে পারত — Integer list-এর invariant ভেঙে। তাই Java generics invariant, flexibility-র জন্য wildcard (? extends Number) দেওয়া হয়েছে। -
Write a generic method
<T> T pickFirst(List<T> xs)that returns the first element — use it with both aList<String>and aList<Integer>.<T> T pickFirst(List<T> xs)generic method লিখুন — String ও Integer list-এ ব্যবহার করে দেখান।✨ Show Answer
Main.javaimport java.util.*; class Main { static <T> T pickFirst(List<T> xs) { return xs.get(0); } public static void main(String[] args) { System.out.println(pickFirst(List.of("Sumi", "Rahim"))); System.out.println(pickFirst(List.of(10, 20, 30))); } } -
Which wildcard would you use for a parameter you only read from? Which for one you only write into?যে parameter থেকে শুধু পড়ছেন — কোন wildcard? যেটিতে শুধু লিখছেন — কোনটি?
✨ Show Answer
Answer: Read-only (producer):
? extends T. Write-only (consumer):? super T. This is the PECS rule — Producer Extends, Consumer Super.পড়লে
? extends T, লিখলে? super T— PECS rule। -
Name one thing you can NOT do at runtime because of type erasure.Type erasure-এর কারণে runtime-এ যা যা করা যায় না তার একটি উল্লেখ করুন।
✨ Show Answer
Answer: You cannot create a generic array (
new T[10]) — the JVM does not know whatTis at runtime, so it cannot pick the array's component type. The common workaround is to create anObject[]and cast, or useList<T>instead.Generic array
new T[10]runtime-এ বানানো যায় না — JVM জানে না T কী। বিকল্প:Object[]বানিয়ে cast, বাList<T>ব্যবহার।
Summary — Module 21
Generics let you write one class or method that works safely with many types — the compiler
enforces type correctness so you never need a cast. Type parameters can be bounded
(T extends Number) so you can call methods on them inside. Wildcards restore
flexibility across subtype relationships: ? extends T for reading (producers) and
? super T for writing (consumers) — the PECS rule. All of this is compile-time
machinery: at runtime, Java erases type parameters, which is why a few operations
(new T[], instanceof List<String>) are forbidden.
T extends Number)। Wildcard: পড়লে ? extends T, লিখলে ? super T — PECS। সবই compile-time; runtime-এ type erase হয়ে যায় — তাই কিছু সীমাবদ্ধতা আছে।