John Squirrels
سطح
San Francisco

قسم erasure

گروپ ۾ شايع ٿيل
سلام اسان generics تي سبق جو سلسلو جاري رکون ٿا. اسان اڳ ۾ هڪ عام خيال حاصل ڪيو آهي ته اهي ڇا آهن ۽ انهن جي ضرورت ڇو آهي. اڄ اسان generics جي ڪجھ خصوصيتن بابت ۽ انھن سان گڏ ڪم ڪرڻ بابت وڌيڪ ڄاڻنداسين. اچو ته هلون! پوئين سبق قسم صاف ڪرڻ - 1 ۾ ، اسان عام قسمن ۽ خام قسمن جي وچ ۾ فرق جي باري ۾ ڳالهايو . خام قسم ھڪڙو عام طبقو آھي جنھن جو قسم ختم ڪيو ويو آھي.
List list = new ArrayList();
هتي هڪ مثال آهي. هتي اسان اهو ظاهر نه ٿا ڪريون ته ڪهڙي قسم جون شيون اسان جي List. جيڪڏهن اسان ڪوشش ڪنداسين ته اهڙي ٺاهي List۽ ان ۾ ڪجهه شيون شامل ڪيون، اسان IDEA ۾ هڪ ڊيڄاريندڙ ڏسندا سين:

"Unchecked call to add(E) as a member of raw type of java.util.List".
پر اسان ان حقيقت جي باري ۾ پڻ ڳالهايو ته جنريڪس صرف جاوا 5 ۾ ظاهر ٿيو. جڏهن هي نسخو جاري ڪيو ويو، پروگرامر اڳ ۾ ئي خام قسم جي استعمال سان ڪوڊ جو هڪ گروپ لکي چڪا هئا، تنهنڪري ٻوليء جي هن خاصيت کي ڪم ڪرڻ کان روڪي نه سگهيو، ۽ قابليت. جاوا ۾ خام قسمون ٺاھيو محفوظ ڪيو ويو. بهرحال، اهو مسئلو وڌيڪ وسيع ٿي ويو. جئين توهان کي خبر آهي، جاوا ڪوڊ هڪ خاص مرتب ٿيل فارميٽ ۾ تبديل ڪيو ويو آهي بائيٽ ڪوڊ، جيڪو پوءِ جاوا ورچوئل مشين ذريعي عمل ڪيو ويندو آهي. پر جيڪڏهن اسان تبادلي جي عمل دوران بائيٽ ڪوڊ ۾ ٽائپ پيرا ميٽرن بابت معلومات رکون ٿا، ته اهو سڀ اڳي لکيل ڪوڊ ٽوڙي ڇڏيندو، ڇاڪاڻ ته جاوا 5 کان اڳ ڪي به قسم جا پيرا ميٽر نه هئا! جڏهن generics سان ڪم ڪرڻ، اتي هڪ تمام اهم تصور آهي ته توهان کي ياد ڪرڻ جي ضرورت آهي. ان کي ٽائپ ايريزر چئبو آهي . ان جو مطلب اهو آهي ته هڪ طبقي ۾ هڪ قسم جي پيٽرولر بابت ڪا ڄاڻ ناهي. اها معلومات صرف گڏ ڪرڻ دوران موجود آهي ۽ ختم ٿي ويندي آهي (ناقابل رسائي) رن ٽائم کان اڳ. جيڪڏهن توهان غلط قسم جي اعتراض کي توهان جي ۾ رکڻ جي ڪوشش ڪندا List<String>، مرتب ڪندڙ هڪ غلطي پيدا ڪندو. اھو اھو آھي جيڪو ٻوليءَ جا ٺاھيندڙ حاصل ڪرڻ چاھين ٿا جڏھن اھي generics ٺاھين: compile-time checks. پر جڏهن توهان جو سمورو جاوا ڪوڊ بائيٽ ڪوڊ ۾ بدلجي وڃي ٿو، ان ۾ هاڻي قسم جي پيرا ميٽرن بابت معلومات نه هوندي آهي. bytecode ۾، توهان جي ٻلين جي فهرست تارن List<Cat>کان مختلف ناهي . List<String>bytecode ۾، ڪجھ به نه ٿو چوي ته شين 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ٽائيپ پيراميٽر کي تبديل ڪري ٿو. اسان پڻ پاس ڪري رهيا آهيون a Double۽ a Stringto طريقو createAndAdd2Values(). ڇا توهان سوچيو ته اسان جو پروگرام ڪم ڪندو؟ آخرڪار، اسان Integerٽائپ آرگيومينٽ طور بيان ڪيو آهي، پر هڪ کي Stringيقيني طور تي ڪاسٽ نٿو ڪري سگهجي Integer! اچو ته main()طريقي سان هلون ۽ چيڪ ڪريو. ڪنسول آئوٽ:

22.111 
Test String
اها غير متوقع هئي! ائين ڇو ٿيو؟ اهو قسم جي ختم ٿيڻ جو نتيجو آهي. Integerاسان جي اعتراض کي تيز ڪرڻ لاء استعمال ٿيل قسم جي دليل بابت معلومات TestClass<Integer> testختم ٿي وئي جڏهن ڪوڊ مرتب ڪيو ويو. ميدان بڻجي ويندو TestClass<Object> test. اسان Double۽ Stringدليلن کي آساني سان Objectشيون ۾ تبديل ڪيو ويو (اهي Integerشيون تبديل نه ڪيا ويا آهن جيئن اسان توقع ڪئي هئي!) ۽ خاموشيء سان شامل ڪيو ويو TestClass. ھتي ھڪڙو ٻيو سادو آھي پر تمام پڌرو مثال قسم erasure جو:
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>، تنهنڪري جڏهن پروگرام هلندو آهي اهو اسان کي ٻڌائي ٿو ته اسان سڀني ٽن ڪيسن ۾ ساڳيو ڪلاس استعمال ڪري رهيا آهيون.

erasure ٽائپ ڪريو جڏهن arrays ۽ generics سان ڪم ڪري رهيا آهيو

هتي هڪ تمام اهم نقطو آهي جيڪو واضح طور تي سمجھڻ گهرجي جڏهن صفن ۽ عام طبقن سان ڪم ڪري رهيو آهي (جهڙوڪ List). توھان کي پڻ ان کي غور ۾ رکڻ گھرجي جڏھن توھان جي پروگرام لاءِ ڊيٽا جي جوڙجڪ کي چونڊيو. Generics قسم جي erasure جي تابع آهن. ٽائيپ پيراميٽرز بابت معلومات رن ٽائم تي دستياب ناهي. ان جي ابتڙ، arrays ڄاڻن ٿا ۽ انهن جي ڊيٽا جي قسم بابت معلومات استعمال ڪري سگھن ٿا جڏهن پروگرام هلندي آهي. هڪ غلط قسم کي صف ۾ رکڻ جي ڪوشش ڪرڻ سان هڪ استثنا اڇلايو ويندو:
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>. لڪير 3 ۾، اسان اسان کي List<String>[]هڪ Object[] objectsمتغير ڏانهن تفويض ڪيو. جاوا ٻولي هن جي اجازت ڏئي ٿي: شين جو هڪ سلسلو سڀني ذيلي طبقن جي شين ۽ شين کي Xذخيرو ڪري سگهي ٿو . انهي جي مطابق، توهان ڪنهن به شيء کي هڪ صف ۾ رکي سگهو ٿا. لڪير 4 ۾، اسان صف جي واحد عنصر کي تبديل ڪريون ٿا (a ) a سان . اهڙيء طرح، اسان هڪ صف ۾ رکون ٿا جيڪو صرف شين کي ذخيرو ڪرڻ جو ارادو هو! اسان کي صرف هڪ غلطي نظر ايندي جڏهن اسان لائن 5 تي عمل ڪنداسين. A کي رن ٽائم تي اڇلايو ويندو. ان جي مطابق، اهڙين صفن جي تخليق تي پابندي جاوا ۾ شامل ڪئي وئي. اهو اسان کي اهڙين حالتن کان بچڻ جي اجازت ڏئي ٿو. XXObjectobjects()List<String>List<Integer>List<Integer>List<String>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
پر هتي هڪ پهلو آهي جنهن بابت اسان نه ڳالهايو آهي. Oracle دستاويزن ۾، توهان ڏسندا ته ڪلاس ڪلاس عام آهي! قسم صاف ڪرڻ - 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اعتراض پيدا ڪرڻ جي قابل هئا! :) ان سان گڏ اڄ جو سبق پڄاڻي تي پهتو. توهان کي هميشه ياد رکڻ گهرجي ٽائپ ايريچر جڏهن عام سان ڪم ڪندي. اهو ڪم تمام آسان نه ٿو لڳي، پر توهان کي اهو سمجهڻ گهرجي ته جنريڪس جاوا ٻولي جو حصو نه هئا جڏهن اها ٺاهي وئي هئي. هي خصوصيت، جيڪا اسان جي مدد ڪري ٿي parameterized مجموعا ٺاهڻ ۽ تاليف دوران غلطيون پڪڙڻ، بعد ۾ ختم ڪيو ويو. ڪجھ ٻين ٻولين ۾ جن ۾ پھرين ورجن کان generics شامل آھن، اتي ڪو به قسم جو خاتمو نه آھي (مثال طور، C# ۾). رستي جي ذريعي، اسان عام اڀياس نه ڪيو آهي! ايندڙ سبق ۾، توهان عام شين جي ڪجهه وڌيڪ خاصيتن کان واقف ٿي ويندا. في الحال، اهو سٺو ٿيندو ته ٻه ڪم حل ڪرڻ! :)
تبصرا
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION