Lesson ১২৭ মিনিট পড়া

জেনেরিকস (Generics)

এমন কোড লিখুন যা যেকোনো টাইপের সাথে কাজ করতে পারে

জেনেরিকস আসলে কোন সমস্যার সমাধান করে (The Problem Generics Solve)

কল্পনা করুন আপনি চমৎকার একটি গিফট বক্স বা বাক্স বানিয়েছেন। একদিন আপনি এর ভেতর একটা বই রাখলেন, আরেকদিন একটা খেলনা, আর অন্য দিন কিছু চকলেট। জেনেরিকস (generics) না থাকলে, জাভা সব ধরনের বাক্সকেই একেই চোখে দেখত — আপনি বাক্সের ভেতরে যাই রাখুন না কেন তা একটি Object হিসেবে বিবেচিত হতো এবং বাক্স থেকে কিছু বের করার সময় আপনাকে অনুমান করে নিতে হতো যে ভেতরে আসলে কী আছে। এটি বেশ ঝামেলার এবং ভুল হওয়ারও একটি বড় কারণ।

জেনেরিকস (Generics) আপনাকে নির্দিষ্ট ধরনের বাক্স তৈরি করতে এবং বলে দিতে সাহায্য করে: "এই বাক্সটি শুধুমাত্র বই রাখার জন্য।" এখন জাভা খুব ভালো করেই জানে এর ভেতরে ঠিক কী আছে, কোনো ভুল হলে সে নিজেই কোড কম্পাইল হওয়ার সময়েই (compile time) সেটি ধরে ফেলে এবং আপনাকে আর কখনোই অনুমান করে নিতে হয় না।

আপনি নিজের অজান্তেই হয়তো এর মধ্যেই জেনেরিকস ব্যবহার করে ফেলেছেন! ArrayList<String> মূলত একটি জেনেরিক ArrayList যা কেবল স্ট্রিং (String) টাইপের ডেটা ধারণ করে। এখানের <String> অংশটিই হলো টাইপ প্যারামিটার (type parameter)

একটি জেনেরিক ক্লাস তৈরি করা (Creating a Generic Class)

public class Main {
// T is a placeholder — it can be ANY type
static class Box<T> {
private T item;
void put(T item) {
this.item = item;
System.out.println("Put in: " + item);
}
T get() {
return item;
}
boolean isEmpty() {
return item == null;
}
}
public static void main(String[] args) {
// A box for Strings
Box<String> nameBox = new Box<>();
nameBox.put("Hello World");
String name = nameBox.get(); // no casting needed!
System.out.println("Got: " + name);
// A box for Integers
Box<Integer> numBox = new Box<>();
numBox.put(42);
int number = numBox.get(); // autoUnboxing
System.out.println("Got: " + number);
// A box for Doubles
Box<Double> priceBox = new Box<>();
priceBox.put(9.99);
System.out.println("Got: " + priceBox.get());
// numBox.put("oops"); // COMPILE ERROR! Type safety!
}
}
Output
Put in: Hello World
Got: Hello World
Put in: 42
Got: 42
Put in: 9.99
Got: 9.99

জেনেরিক মেথড এবং বাউন্ডেড টাইপ (Generic Methods & Bounded Types)

আপনাকে পুরো ক্লাসটিকেই জেনেরিক হতে হবে, এমন কোনো কথা নেই — আপনি চাইলে আলাদা আলাদা মেথডকেও (individual methods) জেনেরিক করতে পারেন। শুধু রিটার্ন টাইপের ঠিক আগে টাইপ প্যারামিটারটি বসিয়ে দিন।

বাউন্ডেড টাইপগুলো (Bounded types) আপনাকে ঠিক কোন কোন টাইপ ব্যবহার করা যাবে, তার একটা সীমানা ঠিক করে দেওয়ার সুযোগ দেয়। যেমন <T extends Number> এর মানে হলো "T এর জায়গার যেকোনো Integer, Double, Float ইত্যাদি বসতে পারে — অর্থাৎ এমন সবকিছু যা মূলত একেকটি নম্বর (Number)।" এই সুবিধাটি আপনাকে জেনেরিক কোডের ভেতরে নম্বর বা Number ক্লাসের বিভিন্ন মেথড, যেমন .doubleValue() ইত্যাদি ব্যবহার করার সুযোগ দেয়।

যখন আপনি "যেকোনো টাইপের" মান গ্রহণ করতে চান কিন্তু সেটির নাম উল্লেখ করার কোনো প্রয়োজন নেই, তখন ওয়াইল্ডকার্ড (Wildcards) (?) ব্যবহার করা হয়। List<?> এর মানে হলো "যে কোনো কিছুর একটি তালিকা বা লিস্ট।" আবার List<? extends Number> এর মানে হলো "যেকোনো ধরনের নম্বর বা Number টাইপের একটি তালিকা।"

জেনেরিক মেথড (Generic Methods)

import java.util.Arrays;
import java.util.List;
public class Main {
// Generic method — works with any array type
static <T> void printArray(T[] array) {
System.out.print("[ ");
for (T item : array) {
System.out.print(item + " ");
}
System.out.println("]");
}
// Bounded type — T must be a Number or subclass
static <T extends Number> double sum(T[] numbers) {
double total = 0;
for (T num : numbers) {
total += num.doubleValue(); // works because T is a Number
}
return total;
}
// Wildcard — accept list of any Number subtype
static double sumList(List<? extends Number> list) {
double total = 0;
for (Number n : list) {
total += n.doubleValue();
}
return total;
}
public static void main(String[] args) {
String[] names = {"Anika", "Rafi", "Sadia"};
Integer[] nums = {1, 2, 3, 4, 5};
Double[] prices = {4.99, 2.50, 8.75};
printArray(names);
printArray(nums);
printArray(prices);
System.out.println("Sum of ints: " + sum(nums));
System.out.println("Sum of doubles: " + sum(prices));
// Wildcard in action
List<Integer> intList = Arrays.asList(10, 20, 30);
List<Double> dblList = Arrays.asList(1.5, 2.5, 3.5);
System.out.println("Int list sum: " + sumList(intList));
System.out.println("Dbl list sum: " + sumList(dblList));
}
}
Output
[ Anika Rafi Sadia ]
[ 1 2 3 4 5 ]
[ 4.99 2.50 8.75 ]
Sum of ints: 15.0
Sum of doubles: 16.24
Int list sum: 60.0
Dbl list sum: 7.5

মাল্টিপল টাইপ প্যারামিটারসহ জেনেরিক ক্লাস (Generic Class with Multiple Type Parameters)

public class Main {
// A Pair holds two values of potentially different types
static class Pair<K, V> {
private K key;
private V value;
Pair(K key, V value) {
this.key = key;
this.value = value;
}
K getKey() { return key; }
V getValue() { return value; }
@Override
public String toString() {
return key + " -> " + value;
}
}
public static void main(String[] args) {
Pair<String, Integer> age = new Pair<>("Anika", 25);
Pair<String, String> color = new Pair<>("Sky", "Blue");
Pair<Integer, Boolean> evenCheck = new Pair<>(7, false);
System.out.println(age);
System.out.println(color);
System.out.println(evenCheck);
// Type-safe access
String name = age.getKey();
int years = age.getValue();
System.out.println(name + " is " + years + " years old");
}
}
Output
Anika -> 25
Sky -> Blue
7 -> false
Alice is 25 years old
Note: এখানে মজার একটা তথ্য বলে রাখি: জেনেরিকসগুলোর অস্তিত্ব কেবল কম্পাইল টাইমে (compile time)-ই থাকে। কোডটি যখন রান করে (runtime) বা চলতে শুরু করে, তখন জাভা এই জেনেরিকসগুলোর টাইপের সমস্ত তথ্য মুছে ফেলে — এই ব্যাপারটিকে বলা হয় টাইপ ইরেজার (type erasure)। কম্পাইল করা কোডে আপনার তৈরি করা Box<String> পরিণত হয় স্রেফ Box-এ। আর এ কারণেই আপনি চাইলেও new T() বা T[] arr = new T[10] এমন কিছু করতে পারবেন না — কারণ জাভা রানটাইমে এসে বুঝতেই পারে না যে এই T জিনিসটা আসলে কী!
চ্যালেঞ্জ

ছোট কুইজ

বলতে কী বোঝায়?
Collections FrameworkException Handling