ওহে! আমরা জেনেরিক বিষয়ে আমাদের পাঠের সিরিজ চালিয়ে যাচ্ছি। আমরা আগে সেগুলি কী এবং কেন তাদের প্রয়োজন সে সম্পর্কে একটি সাধারণ ধারণা পেয়েছি। আজ আমরা জেনেরিকের কিছু বৈশিষ্ট্য এবং তাদের সাথে কাজ করার বিষয়ে আরও জানব। চলো যাই! শেষ পাঠেটাইপ ইরেজার- ১ , আমরা জেনেরিক প্রকার এবং কাঁচা প্রকারের মধ্যে পার্থক্য সম্পর্কে কথা বলেছি । একটি কাঁচা টাইপ হল একটি জেনেরিক ক্লাস যার টাইপ সরানো হয়েছে।

List list = new ArrayList();
এখানে একটি উদাহরণ. এখানে আমরা ইঙ্গিত করি না যে আমাদের কী ধরণের বস্তু স্থাপন করা হবে List। যদি আমরা এমন একটি তৈরি করার চেষ্টা করি Listএবং এতে কিছু বস্তু যোগ করি, তাহলে আমরা IDEA-তে একটি সতর্কতা দেখতে পাব:

"Unchecked call to add(E) as a member of raw type of java.util.List".
কিন্তু আমরা এই বিষয়েও কথা বলেছিলাম যে জেনেরিকগুলি শুধুমাত্র জাভা 5-এ উপস্থিত হয়েছিল। এই সংস্করণটি প্রকাশের সময়, প্রোগ্রামাররা ইতিমধ্যেই কাঁচা ধরনের ব্যবহার করে একগুচ্ছ কোড লিখেছিল, তাই ভাষার এই বৈশিষ্ট্যটি কাজ করা বন্ধ করতে পারেনি, এবং এটি করার ক্ষমতা। জাভাতে কাঁচা প্রকার তৈরি করা সংরক্ষিত ছিল। যাইহোক, সমস্যা আরো ব্যাপক হতে পরিণত. আপনি জানেন যে, জাভা কোডটি বাইটকোড নামে একটি বিশেষ সংকলিত বিন্যাসে রূপান্তরিত হয়, যা জাভা ভার্চুয়াল মেশিন দ্বারা কার্যকর করা হয়। কিন্তু যদি আমরা রূপান্তর প্রক্রিয়ার সময় বাইটকোডে টাইপ প্যারামিটার সম্পর্কে তথ্য রাখি, তবে এটি পূর্বে লিখিত সমস্ত কোড ভেঙ্গে ফেলবে, কারণ জাভা 5 এর আগে কোনও টাইপ প্যারামিটার ছিল না! জেনেরিকের সাথে কাজ করার সময়, একটি খুব গুরুত্বপূর্ণ ধারণা রয়েছে যা আপনাকে মনে রাখতে হবে। একে টাইপ ইরেজার বলা হয়. এর মানে হল যে একটি ক্লাসে একটি টাইপ প্যারামিটার সম্পর্কে কোন তথ্য নেই। এই তথ্যটি শুধুমাত্র সংকলনের সময় পাওয়া যায় এবং রানটাইমের আগে মুছে ফেলা হয় (অগম্য হয়ে যায়)। আপনি যদি আপনার তে ভুল ধরণের অবজেক্ট রাখার চেষ্টা করেন তবে List<String>কম্পাইলার একটি ত্রুটি তৈরি করবে। ভাষার স্রষ্টারা যখন জেনেরিক তৈরি করে তখন ঠিক এটিই অর্জন করতে চায়: কম্পাইল-টাইম চেক। কিন্তু যখন আপনার সমস্ত জাভা কোড বাইটকোডে পরিণত হয়, তখন এতে আর টাইপ প্যারামিটার সম্পর্কে তথ্য থাকে না। বাইটকোডে, আপনার বিড়ালের তালিকা স্ট্রিং List<Cat>থেকে আলাদা নয় । List<String>বাইটকোডে, কিছুই বলে না যে বস্তুর catsএকটি তালিকা Cat। এই ধরনের তথ্য সংকলনের সময় মুছে ফেলা হয় - শুধুমাত্র আপনার কাছে একটি List<Object> catsতালিকা আছে তা প্রোগ্রামের বাইটকোডে শেষ হবে। চলুন দেখি কিভাবে এটি কাজ করে:

public class TestClass<T> {

   private T value1;
   private T value2;

   public void printValues() {
       System.out.println(value1);
       System.out.println(value2);
   }

   public static <T> TestClass<T> createAndAdd2Values(Object o1, Object o2) {
       TestClass<T> result = new TestClass<>();
       result.value1 = (T) o1;
       result.value2 = (T) o2;
       return result;
   }

   public static void main(String[] args) {
       Double d = 22.111;
       String s = "Test String";
       TestClass<Integer> test = createAndAdd2Values(d, s);
       test.printValues();
   }
}
আমরা আমাদের নিজস্ব জেনেরিক TestClassক্লাস তৈরি করেছি। এটি বেশ সহজ: এটি আসলে 2টি বস্তুর একটি ছোট "সংগ্রহ", যা বস্তুটি তৈরি হওয়ার সাথে সাথে সংরক্ষণ করা হয়। এটির 2টি Tক্ষেত্র রয়েছে। যখন createAndAdd2Values()পদ্ধতিটি কার্যকর করা হয়, তখন দুটি পাস করা অবজেক্ট ( Object aএবং Object bঅবশ্যই টাইপে কাস্ট করতে হবে Tএবং তারপরে অবজেক্টে যোগ করতে হবে। TestClassপদ্ধতিতে main(), আমরা একটি তৈরি করি TestClass<Integer>, অর্থাৎ Integerটাইপ আর্গুমেন্ট টাইপ প্যারামিটারকে প্রতিস্থাপন করে । আমরা একটি এবং একটিকেও Integerপাস করছি । পদ্ধতি । আপনি কি মনে করেন আমাদের প্রোগ্রাম কাজ করবে? সর্বোপরি, আমরা টাইপ আর্গুমেন্ট হিসাবে উল্লেখ করেছি, কিন্তু একটি অবশ্যই একটি কাস্ট করা যাবে না ! চলুন রান করা যাকDoubleStringcreateAndAdd2Values()IntegerStringIntegermain()পদ্ধতি এবং চেক। কনসোল আউটপুট:

22.111 
Test String
যে ছিল অপ্রত্যাশিত! এটা কেন হল? এটা টাইপ মুছে ফেলার ফলাফল. কোড কম্পাইল করার সময় Integerআমাদের অবজেক্টকে ইনস্ট্যান্ট করার জন্য ব্যবহৃত টাইপ আর্গুমেন্ট সম্পর্কে তথ্য মুছে ফেলা হয়েছিল। TestClass<Integer> testমাঠ হয়ে যায় TestClass<Object> test। আমাদের Doubleএবং Stringআর্গুমেন্টগুলি সহজেই অবজেক্টে রূপান্তরিত হয়েছিল Object(এগুলি Integerআমাদের প্রত্যাশা অনুযায়ী বস্তুতে রূপান্তরিত হয় না!) এবং শান্তভাবে যোগ করা হয়েছিল TestClass। এখানে টাইপ ইরেজারের আরেকটি সহজ কিন্তু খুব প্রকাশক উদাহরণ রয়েছে:

import java.util.ArrayList;
import java.util.List;

public class Main {

   private class Cat {

   }

   public static void main(String[] args) {

       List<String> strings = new ArrayList<>();
       List<Integer> numbers = new ArrayList<>();
       List<Cat> cats = new ArrayList<>();

       System.out.println(strings.getClass() == numbers.getClass());
       System.out.println(numbers.getClass() == cats.getClass());

   }
}
কনসোল আউটপুট:

true 
true
মনে হচ্ছে আমরা তিনটি ভিন্ন ধরনের আর্গুমেন্ট দিয়ে সংগ্রহ তৈরি করেছি — String, Integerএবং আমাদের নিজস্ব Catক্লাস। কিন্তু বাইটকোডে রূপান্তরের সময়, তিনটি তালিকাই হয়ে যায় List<Object>, তাই যখন প্রোগ্রামটি চলে তখন এটি আমাদের বলে যে আমরা তিনটি ক্ষেত্রেই একই ক্লাস ব্যবহার করছি।

অ্যারে এবং জেনেরিকের সাথে কাজ করার সময় ইরেজার টাইপ করুন

অ্যারে এবং জেনেরিক ক্লাস (যেমন List) এর সাথে কাজ করার সময় একটি খুব গুরুত্বপূর্ণ বিষয় রয়েছে যা স্পষ্টভাবে বোঝা উচিত। আপনার প্রোগ্রামের জন্য ডেটা স্ট্রাকচার বেছে নেওয়ার সময় আপনার এটি বিবেচনায় নেওয়া উচিত। জেনেরিক টাইপ ইরেজার সাপেক্ষে। টাইপ প্যারামিটার সম্পর্কে তথ্য রানটাইমে পাওয়া যায় না। বিপরীতে, অ্যারেগুলি প্রোগ্রামটি চলাকালীন তাদের ডেটা প্রকার সম্পর্কে জানে এবং ব্যবহার করতে পারে। একটি অ্যারেতে একটি অবৈধ টাইপ রাখার চেষ্টা করলে একটি ব্যতিক্রম নিক্ষেপ করা হবে:

public class Main2 {

   public static void main(String[] args) {

       Object x[] = new String[3];
       x[0] = new Integer(222);
   }
}
কনসোল আউটপুট:

Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer
কারণ অ্যারে এবং জেনেরিকের মধ্যে এত বড় পার্থক্য রয়েছে, তাদের সামঞ্জস্যের সমস্যা থাকতে পারে। সর্বোপরি, আপনি জেনেরিক বস্তুর একটি অ্যারে তৈরি করতে পারবেন না এমনকি একটি প্যারামিটারাইজড অ্যারেও তৈরি করতে পারবেন না। যে একটু বিভ্রান্তিকর শব্দ? একবার দেখা যাক. উদাহরণস্বরূপ, আপনি জাভাতে এর কোনোটি করতে পারবেন না:

new List<T>[]
new List<String>[]
new T[]
যদি আমরা অবজেক্টের একটি অ্যারে তৈরি করার চেষ্টা করি List<String>, আমরা একটি সংকলন ত্রুটি পাই যা জেনেরিক অ্যারে তৈরির বিষয়ে অভিযোগ করে:

import java.util.List;

public class Main2 {

   public static void main(String[] args) {

       // Compilation error! Generic array creation
       List<String>[] stringLists = new List<String>[1];
   }
}
কিন্তু কেন এটা করা হয়? কেন এই ধরনের অ্যারে তৈরির অনুমতি দেওয়া হয় না? এই সব ধরনের নিরাপত্তা প্রদান. যদি কম্পাইলার আমাদের জেনেরিক বস্তুর এই ধরনের অ্যারে তৈরি করতে দেয়, আমরা নিজেদের জন্য অনেক সমস্যা তৈরি করতে পারি। এখানে জোশুয়া ব্লচের বই "কার্যকর জাভা" থেকে একটি সাধারণ উদাহরণ রয়েছে:

public static void main(String[] args) {

   List<String>[] stringLists = new List<String>[1];  //  (1)
   List<Integer> intList = Arrays.asList(42, 65, 44);  //  (2)
   Object[] objects = stringLists;  //  (3)
   objects[0] = intList;  //  (4)
   String s = stringLists[0].get(0);  //  (5)
}
আসুন কল্পনা করি যে একটি অ্যারে তৈরি করার List<String>[] stringListsঅনুমতি দেওয়া হয়েছে এবং এটি একটি সংকলন ত্রুটি তৈরি করবে না। যদি এটি সত্য হয় তবে এখানে কিছু জিনিস রয়েছে যা আমরা করতে পারি: লাইন 1 এ, আমরা তালিকার একটি অ্যারে তৈরি করি List<String>[] stringLists: আমাদের অ্যারেতে একটি রয়েছে List<String>। লাইন 2 এ, আমরা সংখ্যার একটি তালিকা তৈরি করি List<Integer>: List<String>[]লাইন 3 এ, আমরা একটি ভেরিয়েবলে আমাদের বরাদ্দ করি Object[] objects। জাভা ভাষা এটির অনুমতি দেয়: অবজেক্টের একটি অ্যারে সমস্ত সাবক্লাসের অবজেক্ট এবং অবজেক্ট Xসংরক্ষণ করতে পারে । তদনুসারে, আপনি একটি অ্যারের মধ্যে সব কিছু রাখতে পারেন . লাইন 4 এ, আমরা অ্যারের একমাত্র উপাদান (a ) এর সাথে প্রতিস্থাপন করি । এইভাবে, আমরা একটি অ্যারে রাখি যা শুধুমাত্র সঞ্চয় করার উদ্দেশ্যে ছিলXXObjectobjects()List<String>List<Integer>List<Integer>List<String>বস্তু আমরা একটি ত্রুটির সম্মুখীন হব যখন আমরা লাইন 5 কার্যকর করব। A ClassCastExceptionরানটাইমে নিক্ষেপ করা হবে। তদনুসারে, জাভাতে এই জাতীয় অ্যারে তৈরির উপর একটি নিষেধাজ্ঞা যুক্ত করা হয়েছিল। এটি আমাদের এই ধরনের পরিস্থিতি এড়াতে দেয়।

আমি কিভাবে টাইপ ইরেজার কাছাকাছি পেতে পারি?

আচ্ছা, আমরা টাইপ ইরেজার সম্পর্কে শিখেছি। এর সিস্টেম চালাকি করার চেষ্টা করা যাক! :) টাস্ক: আমাদের একটি জেনেরিক TestClass<T>ক্লাস আছে। আমরা এই শ্রেণীর জন্য একটি পদ্ধতি লিখতে চাই যা একটি নতুন অবজেক্ট createNewT()তৈরি করবে এবং ফেরত দেবে । Tকিন্তু এটা অসম্ভব, তাই না? কম্পাইলেশনের সময় টাইপ সম্পর্কে সমস্ত তথ্য Tমুছে ফেলা হয়, এবং রানটাইমে আমরা নির্ধারণ করতে পারি না যে আমাদের কী ধরনের অবজেক্ট তৈরি করতে হবে। এটি করার জন্য আসলে একটি কঠিন উপায় আছে। আপনি সম্ভবত জাভা একটি ক্লাস আছে মনে রাখবেন Class. আমরা আমাদের যে কোনো বস্তুর শ্রেণী নির্ধারণ করতে এটি ব্যবহার করতে পারি:

public class Main2 {

   public static void main(String[] args) {

       Class classInt = Integer.class;
       Class classString = String.class;

       System.out.println(classInt);
       System.out.println(classString);
   }
}
কনসোল আউটপুট:

class java.lang.Integer 
class java.lang.String
কিন্তু এখানে একটা দিক আছে যেটা নিয়ে আমরা কথা বলিনি। ওরাকল ডকুমেন্টেশনে, আপনি দেখতে পাবেন যে ক্লাস ক্লাস জেনেরিক! টাইপ ইরেজার - 3

https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html

ডকুমেন্টেশন বলছে, "T - এই ক্লাস অবজেক্ট দ্বারা মডেল করা ক্লাসের ধরন।" ডকুমেন্টেশনের ভাষা থেকে সরল বক্তৃতায় এটি অনুবাদ করে, আমরা বুঝতে পারি যে বস্তুর শ্রেণিটি Integer.classকেবল নয় Class, বরং Class<Integer>। বস্তুর ধরনটি String.classশুধু নয় Class, বরং Class<String>, ইত্যাদি। যদি এটি এখনও পরিষ্কার না হয়, তাহলে পূর্ববর্তী উদাহরণে একটি টাইপ প্যারামিটার যোগ করার চেষ্টা করুন:

public class Main2 {

   public static void main(String[] args) {

       Class<Integer> classInt = Integer.class;
       // Compilation error!
       Class<String> classInt2 = Integer.class;
      
      
       Class<String> classString = String.class;
       // Compilation error!
       Class<Double> classString2 = String.class;
   }
}
এবং এখন, এই জ্ঞান ব্যবহার করে, আমরা টাইপ ইরেজার বাইপাস করতে পারি এবং আমাদের কাজটি সম্পন্ন করতে পারি! আসুন একটি টাইপ প্যারামিটার সম্পর্কে তথ্য পেতে চেষ্টা করি। আমাদের টাইপ আর্গুমেন্ট হবে MySecretClass:

public class MySecretClass {

   public MySecretClass() {

       System.out.println("A MySecretClass object was created successfully!");
   }
}
এবং এখানে আমরা কীভাবে আমাদের সমাধানটি অনুশীলনে ব্যবহার করি:

public class TestClass<T> {

   Class<T> typeParameterClass;

   public TestClass(Class<T> typeParameterClass) {
       this.typeParameterClass = typeParameterClass;
   }

   public T createNewT() throws IllegalAccessException, InstantiationException {
       T t = typeParameterClass.newInstance();
       return t;
   }

   public static void main(String[] args) throws InstantiationException, IllegalAccessException {

       TestClass<MySecretClass> testString = new TestClass<>(MySecretClass.class);
       MySecretClass secret = testString.createNewT();

   }
}
কনসোল আউটপুট:

A MySecretClass object was created successfully!
আমরা আমাদের জেনেরিক ক্লাসের কনস্ট্রাক্টরের কাছে প্রয়োজনীয় ক্লাস আর্গুমেন্ট পাস করেছি:

TestClass<MySecretClass> testString = new TestClass<>(MySecretClass.class);
এটি আমাদের টাইপ আর্গুমেন্ট সম্পর্কে তথ্য সংরক্ষণ করার অনুমতি দেয়, এটি সম্পূর্ণরূপে মুছে ফেলা থেকে প্রতিরোধ করে। ফলস্বরূপ, আমরা একটি তৈরি করতে সক্ষম হয়েছিTবস্তু :) সেই সাথে, আজকের পাঠ শেষ হয়। জেনেরিকের সাথে কাজ করার সময় আপনাকে সর্বদা টাইপ ইরেজার মনে রাখতে হবে। এই সমাধানটি খুব সুবিধাজনক বলে মনে হচ্ছে না, তবে আপনার বোঝা উচিত যে জেনেরিকগুলি জাভা ভাষার অংশ ছিল না যখন এটি তৈরি করা হয়েছিল। এই বৈশিষ্ট্যটি, যা আমাদের প্যারামিটারাইজড সংগ্রহ তৈরি করতে এবং সংকলনের সময় ত্রুটিগুলি ধরতে সহায়তা করে, পরে তা মোকাবেলা করা হয়েছিল। প্রথম সংস্করণ থেকে জেনেরিক অন্তর্ভুক্ত অন্যান্য কিছু ভাষায়, কোন প্রকার ইরেজার নেই (উদাহরণস্বরূপ, C# এ)। যাইহোক, আমরা জেনেরিক অধ্যয়ন শেষ করিনি! পরবর্তী পাঠে, আপনি জেনেরিকের আরও কয়েকটি বৈশিষ্ট্যের সাথে পরিচিত হবেন। আপাতত দু-একটা কাজ সমাধান করলে ভালো হবে! :)