ভূমিকা
JSE 5.0 দিয়ে শুরু করে, জাভা ভাষার অস্ত্রাগারে জেনেরিক যোগ করা হয়েছিল।
জাভাতে জেনেরিক কি?
জেনেরিক হ'ল জেনেরিক প্রোগ্রামিং বাস্তবায়নের জন্য জাভার বিশেষ ব্যবস্থা — ডেটা এবং অ্যালগরিদমগুলি বর্ণনা করার একটি উপায় যা আপনাকে অ্যালগরিদমের বর্ণনা পরিবর্তন না করেই বিভিন্ন ডেটাটাইপের সাথে কাজ করতে দেয়। ওরাকল ওয়েবসাইটে জেনেরিকের জন্য নিবেদিত একটি পৃথক টিউটোরিয়াল রয়েছে: "
পাঠ "। জেনেরিক বোঝার জন্য, আপনাকে প্রথমে তাদের কেন প্রয়োজন এবং তারা কী দেয় তা খুঁজে বের করতে হবে। টিউটোরিয়ালের "
কেন জেনেরিক ব্যবহার করুন? " বিভাগটি বলে যে কয়েকটি উদ্দেশ্য হল কম্পাইলের সময় শক্তিশালী টাইপ চেক করা এবং স্পষ্ট কাস্টের প্রয়োজনীয়তা দূর করা।
আসুন আমাদের প্রিয়
Tutorialspoint অনলাইন জাভা কম্পাইলারে কিছু পরীক্ষার জন্য প্রস্তুত করা যাক। ধরুন আপনার নিম্নলিখিত কোড আছে:
import java.util.*;
public class HelloWorld {
public static void main(String []args) {
List list = new ArrayList();
list.add("Hello");
String text = list.get(0) + ", world!";
System.out.print(text);
}
}
এই কোড পুরোপুরি ভাল চালানো হবে. কিন্তু বস যদি আমাদের কাছে এসে বলে যে "হ্যালো, বিশ্ব!" একটি অত্যধিক ব্যবহৃত বাক্যাংশ এবং যে আপনি শুধুমাত্র "হ্যালো" ফেরত দিতে হবে? আমরা কোডটি সরিয়ে দেব যা
", বিশ্ব!" এই যথেষ্ট ক্ষতিকারক মনে হচ্ছে, তাই না? কিন্তু আমরা আসলে কম্পাইল টাইমে একটি ত্রুটি পাই:
error: incompatible types: Object cannot be converted to String
সমস্যা হল যে আমাদের তালিকায় বস্তু সঞ্চয় করে।
স্ট্রিং হল অবজেক্টের একটি বংশধর (যেহেতু সমস্ত জাভা শ্রেণী অন্তর্নিহিতভাবে
অবজেক্টের উত্তরাধিকারী হয় ), যার মানে আমাদের একটি স্পষ্ট কাস্ট প্রয়োজন, কিন্তু আমরা একটি যোগ করিনি। কনক্যাটেনেশন অপারেশন চলাকালীন, অবজেক্ট ব্যবহার করে স্ট্যাটিক
String.valueOf(obj) পদ্ধতি কল করা হবে। অবশেষে, এটি অবজেক্ট ক্লাসের
toString পদ্ধতিকে কল করবে । অন্য কথায়, আমাদের
তালিকায় একটি অবজেক্ট রয়েছে । এর মানে হল যে যেখানেই আমাদের একটি নির্দিষ্ট প্রকারের প্রয়োজন (
অবজেক্ট নয় ), আমাদের টাইপ রূপান্তর নিজেদের করতে হবে:
import java.util.*;
public class HelloWorld {
public static void main(String []args) {
List list = new ArrayList();
list.add("Hello!");
list.add(123);
for (Object str : list) {
System.out.println("-" + (String)str);
}
}
}
যাইহোক, এই ক্ষেত্রে, যেহেতু
List অবজেক্ট নেয়, এটি শুধুমাত্র
String s নয়,
Integer sও সংরক্ষণ করতে পারে। কিন্তু সবচেয়ে খারাপ জিনিস হল যে কম্পাইলার এখানে কিছু ভুল দেখতে পায় না। এবং এখন আমরা RUN টাইমে একটি ত্রুটি পাব (একটি "রানটাইম ত্রুটি" হিসাবে পরিচিত)। ত্রুটি হবে:
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
আপনাকে অবশ্যই একমত হতে হবে যে এটি খুব ভাল নয়। এবং এই সমস্ত কারণ কম্পাইলার একটি কৃত্রিম বুদ্ধিমত্তা নয় যা সর্বদা সঠিকভাবে প্রোগ্রামারের উদ্দেশ্য অনুমান করতে সক্ষম। Java SE 5 জেনেরিক প্রবর্তন করেছে যাতে আমরা কম্পাইলারকে আমাদের উদ্দেশ্য সম্পর্কে বলতে পারি — আমরা কোন ধরনের ব্যবহার করতে যাচ্ছি। আমরা যা চাই তা কম্পাইলারকে বলে আমরা আমাদের কোড ঠিক করি:
import java.util.*;
public class HelloWorld {
public static void main(String []args) {
List<String> list = new ArrayList<>();
list.add("Hello!");
list.add(123);
for (Object str : list) {
System.out.println("-" + str);
}
}
}
আপনি দেখতে পাচ্ছেন, আমাদের আর স্ট্রিং -এ কাস্টের প্রয়োজন নেই । উপরন্তু, আমরা টাইপ আর্গুমেন্ট ঘিরে কোণ বন্ধনী আছে. এখন কম্পাইলার আমাদের ক্লাস কম্পাইল করতে দেবে না যতক্ষণ না আমরা তালিকায় 123 যোগ করে এমন লাইনটি অপসারণ করি, যেহেতু এটি একটি
পূর্ণসংখ্যা । এবং এটা আমাদের তাই বলবে. অনেকে জেনেরিককে "সিনট্যাটিক সুগার" বলে থাকেন। এবং তারা সঠিক, যেহেতু জেনেরিকগুলি সংকলিত হওয়ার পরে, তারা সত্যিই একই ধরণের রূপান্তর হয়ে ওঠে। আসুন কম্পাইল করা ক্লাসের বাইটকোড দেখি: একটি যেটি একটি স্পষ্ট কাস্ট ব্যবহার করে এবং একটি যা জেনেরিক ব্যবহার করে:
সংকলনের পরে, সমস্ত জেনেরিক মুছে ফেলা হয়। একে "
টাইপ ইরেজার" বলা হয়"। টাইপ ইরেজার এবং জেনেরিকগুলিকে JDK-এর পুরানো সংস্করণগুলির সাথে পিছিয়ে সামঞ্জস্যপূর্ণ করার জন্য ডিজাইন করা হয়েছে এবং একই সাথে কম্পাইলারকে জাভা-এর নতুন সংস্করণগুলিতে টাইপ সংজ্ঞায় সহায়তা করার অনুমতি দেয়৷
কাঁচা প্রকার
জেনেরিকের কথা বললে, আমাদের সর্বদা দুটি বিভাগ থাকে: প্যারামিটারাইজড প্রকার এবং কাঁচা প্রকার। কাঁচা প্রকারগুলি হল সেই প্রকারগুলি যেগুলি কোণ বন্ধনীতে "টাইপ স্পষ্টীকরণ" বাদ দেয়:
প্যারামিটারাইজড প্রকারগুলি, হাতে, একটি "স্পষ্টীকরণ" অন্তর্ভুক্ত করে:
আপনি দেখতে পাচ্ছেন, আমরা স্ক্রিনশটে একটি তীর দ্বারা চিহ্নিত একটি অস্বাভাবিক গঠন ব্যবহার করেছি। এটি একটি বিশেষ সিনট্যাক্স যা Java SE 7-এ যোগ করা হয়েছে। একে "
হীরা " বলা হয়। কেন? কোণ বন্ধনী একটি হীরা গঠন করে:
<> ।
আপনার আরও জানা উচিত যে ডায়মন্ড সিনট্যাক্স " টাইপ ইনফারেন্স " ধারণার সাথে যুক্ত । সর্বোপরি, কম্পাইলার,
<> দেখেডানদিকে, অ্যাসাইনমেন্ট অপারেটরের বাম দিকে তাকায়, যেখানে এটি ভেরিয়েবলের ধরন খুঁজে পায় যার মান নির্ধারণ করা হচ্ছে। এটি এই অংশে যা খুঁজে পায় তার উপর ভিত্তি করে, এটি ডানদিকের মানটির ধরন বোঝে। প্রকৃতপক্ষে, যদি একটি জেনেরিক টাইপ বাম দিকে দেওয়া হয়, তবে ডানদিকে নয়, কম্পাইলার টাইপটি অনুমান করতে পারে:
import java.util.*;
public class HelloWorld {
public static void main(String []args) {
List<String> list = new ArrayList();
list.add("Hello, World");
String data = list.get(0);
System.out.println(data);
}
}
তবে এটি জেনেরিকের সাথে নতুন শৈলী এবং সেগুলি ছাড়া পুরানো শৈলীকে মিশ্রিত করে। এবং এটি অত্যন্ত অবাঞ্ছিত। উপরের কোডটি কম্পাইল করার সময়, আমরা নিম্নলিখিত বার্তাটি পাই:
Note: HelloWorld.java uses unchecked or unsafe operations
আসলে, কেন আপনাকে এখানে একটি হীরা যোগ করতে হবে তা বোধগম্য বলে মনে হচ্ছে। কিন্তু এখানে একটি উদাহরণ:
import java.util.*;
public class HelloWorld {
public static void main(String []args) {
List<String> list = Arrays.asList("Hello", "World");
List<Integer> data = new ArrayList(list);
Integer intNumber = data.get(0);
System.out.println(data);
}
}
আপনি মনে করবেন যে
ArrayList এর একটি দ্বিতীয় কনস্ট্রাক্টর রয়েছে যা একটি আর্গুমেন্ট হিসাবে একটি সংগ্রহ নেয়। আর এখানেই অশুভ কিছু লুকিয়ে আছে। ডায়মন্ড সিনট্যাক্স ছাড়া, কম্পাইলার বুঝতে পারে না যে এটি প্রতারিত হচ্ছে। ডায়মন্ড সিনট্যাক্স দিয়ে, এটা করে। সুতরাং, নিয়ম #1 হল: সর্বদা প্যারামিটারাইজড প্রকারের সাথে ডায়মন্ড সিনট্যাক্স ব্যবহার করুন। অন্যথায়, যেখানে আমরা কাঁচা ধরনের ব্যবহার করছি সেখানে আমরা হারিয়ে যাওয়ার ঝুঁকি নিয়ে থাকি। "অচেক করা বা অনিরাপদ অপারেশন ব্যবহার করে" সতর্কতা দূর করতে, আমরা একটি পদ্ধতি বা ক্লাসে
@SuppressWarnings("unchecked") টীকা ব্যবহার করতে পারি। কিন্তু আপনি কেন এটি ব্যবহার করার সিদ্ধান্ত নিয়েছেন তা নিয়ে ভাবুন। নিয়ম নম্বর এক মনে রাখবেন। হয়তো আপনাকে একটি টাইপ আর্গুমেন্ট যোগ করতে হবে।
জাভা জেনেরিক পদ্ধতি
জেনেরিক আপনাকে এমন পদ্ধতি তৈরি করতে দেয় যার প্যারামিটারের ধরন এবং রিটার্ন টাইপ প্যারামিটারাইজড। ওরাকল টিউটোরিয়ালে এই ক্ষমতার জন্য একটি পৃথক বিভাগ উৎসর্গ করা হয়েছে: "
জেনেরিক পদ্ধতি "। এই টিউটোরিয়ালে শেখানো সিনট্যাক্স মনে রাখা গুরুত্বপূর্ণ:
- এটি কোণ বন্ধনীর ভিতরে টাইপ পরামিতিগুলির একটি তালিকা অন্তর্ভুক্ত করে;
- টাইপ প্যারামিটারের তালিকা পদ্ধতির রিটার্ন টাইপের আগে চলে যায়।
আসুন একটি উদাহরণ দেখি:
import java.util.*;
public class HelloWorld {
public static class Util {
public static <T> T getValue(Object obj, Class<T> clazz) {
return (T) obj;
}
public static <T> T getValue(Object obj) {
return (T) obj;
}
}
public static void main(String []args) {
List list = Arrays.asList("Author", "Book");
for (Object element : list) {
String data = Util.getValue(element, String.class);
System.out.println(data);
System.out.println(Util.<String>getValue(element));
}
}
}
আপনি যদি Util ক্লাসটি দেখেন , আপনি দেখতে পাবেন যে এটির দুটি জেনেরিক পদ্ধতি রয়েছে। টাইপ ইনফারেন্সের সম্ভাবনার জন্য ধন্যবাদ, আমরা হয় টাইপটি সরাসরি কম্পাইলারকে নির্দেশ করতে পারি, অথবা আমরা নিজেরাই এটি নির্দিষ্ট করতে পারি। উভয় বিকল্প উদাহরণে উপস্থাপিত হয়. যাইহোক, সিনট্যাক্স অনেক জ্ঞান করে তোলে যদি আপনি এটি সম্পর্কে চিন্তা করেন। একটি জেনেরিক পদ্ধতি ঘোষণা করার সময়, আমরা পদ্ধতির আগে টাইপ প্যারামিটারটি নির্দিষ্ট করি, কারণ যদি আমরা পদ্ধতির পরে টাইপ প্যারামিটার ঘোষণা করি, তাহলে JVM কোন প্রকার ব্যবহার করতে হবে তা বের করতে সক্ষম হবে না।
তদনুসারে, আমরা প্রথমে ঘোষণা করি যে আমরা T টাইপ প্যারামিটার ব্যবহার করব , এবং তারপর আমরা বলি যে আমরা এই টাইপটি ফেরত দিতে যাচ্ছি। স্বাভাবিকভাবেই,
Util.<Integer>getValue(element, String.class) একটি ত্রুটির সাথে ব্যর্থ হবে:
অসামঞ্জস্যপূর্ণ প্রকার: Class<String> কে Class<Integer> এ রূপান্তর করা যাবে না । জেনেরিক পদ্ধতি ব্যবহার করার সময়, আপনার সর্বদা টাইপ ইরেজার মনে রাখা উচিত। আসুন একটি উদাহরণ দেখি:
import java.util.*;
public class HelloWorld {
public static class Util {
public static <T> T getValue(Object obj) {
return (T) obj;
}
}
public static void main(String []args) {
List list = Arrays.asList(2, 3);
for (Object element : list) {
System.out.println(Util.<Integer>getValue(element) + 1);
}
}
}
এই ঠিক ঠিক চালানো হবে. কিন্তু যতক্ষণ না কম্পাইলার বুঝতে পারে যে মেথডটির রিটার্ন টাইপটি হল
Integer । নিম্নলিখিত লাইন দিয়ে কনসোল আউটপুট বিবৃতি প্রতিস্থাপন করুন:
System.out.println(Util.getValue(element) + 1);
আমরা একটি ত্রুটি পেয়েছি:
bad operand types for binary operator '+', first type: Object, second type: int.
অন্য কথায়, টাইপ ইরেজার ঘটেছে। কম্পাইলার দেখে যে কেউ টাইপটি নির্দিষ্ট করেনি, তাই টাইপটিকে
অবজেক্ট হিসাবে নির্দেশ করা হয়েছে এবং পদ্ধতিটি একটি ত্রুটির সাথে ব্যর্থ হয়।
জেনেরিক ক্লাস
শুধুমাত্র পদ্ধতিগুলিকে প্যারামিটারাইজ করা যায় না। ক্লাসও করা যায়। ওরাকলের টিউটোরিয়ালের
"জেনারিক টাইপস" বিভাগটি এটির জন্য উত্সর্গীকৃত। আসুন একটি উদাহরণ বিবেচনা করা যাক:
public static class SomeType<T> {
public <E> void test(Collection<E> collection) {
for (E element : collection) {
System.out.println(element);
}
}
public void test(List<Integer> collection) {
for (Integer element : collection) {
System.out.println(element);
}
}
}
এখানে সবকিছু সহজ. যদি আমরা জেনেরিক ক্লাস ব্যবহার করি, টাইপ প্যারামিটারটি ক্লাসের নামের পরে নির্দেশিত হয়।
এখন মূল পদ্ধতিতে এই ক্লাসের একটি উদাহরণ তৈরি করা যাক :
public static void main(String []args) {
SomeType<String> st = new SomeType<>();
List<String> list = Arrays.asList("test");
st.test(list);
}
এই কোড ভালো চলবে।
কম্পাইলার দেখে যে সংখ্যার একটি
তালিকা এবং স্ট্রিংগুলির একটি
সংগ্রহ রয়েছে । কিন্তু আমরা যদি টাইপ প্যারামিটারটি মুছে ফেলি এবং এটি করি তবে কী হবে:
SomeType st = new SomeType();
List<String> list = Arrays.asList("test");
st.test(list);
আমরা একটি ত্রুটি পেয়েছি:
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
আবার, এটি টাইপ ইরেজার। যেহেতু ক্লাসটি আর টাইপ প্যারামিটার ব্যবহার করে না, তাই কম্পাইলার সিদ্ধান্ত নেয় যে, যেহেতু আমরা একটি
List পাস করেছি, তাই List<Integer> এর সাথে পদ্ধতিটি সবচেয়ে উপযুক্ত। এবং আমরা একটি ত্রুটি সঙ্গে ব্যর্থ. অতএব, আমাদের নিয়ম #2 আছে: যদি আপনার একটি জেনেরিক ক্লাস থাকে তবে সর্বদা প্রকারের পরামিতিগুলি নির্দিষ্ট করুন।
বিধিনিষেধ
আমরা জেনেরিক পদ্ধতি এবং শ্রেণীতে নির্দিষ্ট ধরনের সীমাবদ্ধ করতে পারি। উদাহরণস্বরূপ, ধরুন আমরা একটি কন্টেইনার চাই যে টাইপ আর্গুমেন্ট হিসাবে শুধুমাত্র একটি
সংখ্যা গ্রহণ করুক। ওরাকলের টিউটোরিয়ালের
বাউন্ডেড টাইপ প্যারামিটার বিভাগে এই বৈশিষ্ট্যটি বর্ণনা করা হয়েছে । আসুন একটি উদাহরণ দেখি:
import java.util.*;
public class HelloWorld {
public static class NumberContainer<T extends Number> {
private T number;
public NumberContainer(T number) { this.number = number; }
public void print() {
System.out.println(number);
}
}
public static void main(String []args) {
NumberContainer number1 = new NumberContainer(2L);
NumberContainer number2 = new NumberContainer(1);
NumberContainer number3 = new NumberContainer("f");
}
}
আপনি দেখতে পাচ্ছেন, আমরা টাইপ প্যারামিটারটি
সংখ্যা শ্রেণী/ইন্টারফেস বা এর বংশধরদের মধ্যে সীমাবদ্ধ করেছি। মনে রাখবেন যে আপনি শুধুমাত্র একটি ক্লাস নয়, ইন্টারফেসও নির্দিষ্ট করতে পারেন। উদাহরণ স্বরূপ:
public static class NumberContainer<T extends Number & Comparable> {
জেনেরিক এছাড়াও
ওয়াইল্ডকার্ড সমর্থন করে তারা তিন প্রকারে বিভক্ত:
আপনার ওয়াইল্ডকার্ডের ব্যবহার
গেট-পুট নীতি মেনে চলা উচিত । এটি নিম্নরূপ প্রকাশ করা যেতে পারে:
- একটি প্রসারিত ওয়াইল্ডকার্ড ব্যবহার করুন যখন আপনি শুধুমাত্র একটি কাঠামোর বাইরে মান পাবেন।
- একটি সুপার ওয়াইল্ডকার্ড ব্যবহার করুন যখন আপনি শুধুমাত্র একটি কাঠামোতে মান রাখেন।
- এবং যখন আপনি উভয়ই একটি কাঠামো থেকে পেতে এবং রাখতে চান তখন ওয়াইল্ডকার্ড ব্যবহার করবেন না।
এই নীতিটিকে প্রযোজক এক্সটেনডস কনজিউমার সুপার (PECS) নীতিও বলা হয়।
এখানে জাভা এর Collections.copy পদ্ধতির জন্য সোর্স কোড থেকে একটি ছোট উদাহরণ :
এবং এখানে কি কাজ করবে না তার একটি ছোট উদাহরণ:
public static class TestClass {
public static void print(List<? extends String> list) {
list.add("Hello, World!");
System.out.println(list.get(0));
}
}
public static void main(String []args) {
List<String> list = new ArrayList<>();
TestClass.print(list);
}
কিন্তু যদি আপনি
সুপার দিয়ে
এক্সটেন্ড প্রতিস্থাপন করেন , তাহলে সবকিছু ঠিক আছে। কারণ আমরা তালিকার বিষয়বস্তু প্রদর্শনের আগে একটি মান দিয়ে
তালিকাটি তৈরি করি , এটি একটি
ভোক্তা ৷ সেই অনুযায়ী, আমরা সুপার ব্যবহার করি।
উত্তরাধিকার
জেনেরিকের আরেকটি আকর্ষণীয় বৈশিষ্ট্য রয়েছে: উত্তরাধিকার। জেনেরিকের জন্য যেভাবে উত্তরাধিকার কাজ করে তা ওরাকলের টিউটোরিয়ালের "
জেনারিকস, ইনহেরিট্যান্স এবং সাবটাইপস " এর অধীনে বর্ণনা করা হয়েছে। গুরুত্বপূর্ণ বিষয় হল নিম্নলিখিত মনে রাখা এবং চিনতে হবে। আমরা এটি করতে পারি না:
List<CharSequence> list1 = new ArrayList<String>();
কারণ উত্তরাধিকার জেনেরিকের সাথে ভিন্নভাবে কাজ করে:
এবং এখানে আরেকটি ভাল উদাহরণ যা একটি ত্রুটির সাথে ব্যর্থ হবে:
List<String> list1 = new ArrayList<>();
List<Object> list2 = list1;
আবার, এখানে সবকিছু সহজ।
List<String> List<Object> এর বংশধর নয় , যদিও
স্ট্রিং অবজেক্টের একটি বংশধর ।
আপনি যা শিখেছেন তা শক্তিশালী করার জন্য, আমরা আপনাকে আমাদের জাভা কোর্স থেকে একটি ভিডিও পাঠ দেখার পরামর্শ দিই
উপসংহার
তাই আমরা জেনেরিক বিষয়ে আমাদের স্মৃতি রিফ্রেশ করেছি। আপনি যদি খুব কমই তাদের ক্ষমতার সম্পূর্ণ সদ্ব্যবহার করেন, তবে কিছু বিবরণ অস্পষ্ট হয়ে যায়। আমি আশা করি এই সংক্ষিপ্ত পর্যালোচনা আপনার মেমরি জগ সাহায্য করেছে. আরও ভাল ফলাফলের জন্য, আমি দৃঢ়ভাবে সুপারিশ করছি যে আপনি নিম্নলিখিত উপাদানগুলির সাথে নিজেকে পরিচিত করুন:
GO TO FULL VERSION