CodeGym /جاوا بلاگ /Random-UR /مٹانا ٹائپ کریں۔
John Squirrels
سطح
San Francisco

مٹانا ٹائپ کریں۔

گروپ میں شائع ہوا۔
ہائے! ہم 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 سے پہلے کوئی قسم کے پیرامیٹرز نہیں تھے! جنریکس کے ساتھ کام کرتے وقت، ایک بہت اہم تصور ہے جو آپ کو یاد رکھنے کی ضرورت ہے۔ اسے ٹائپ ایریزر کہتے ہیں ۔ اس کا مطلب ہے کہ کلاس میں ٹائپ پیرامیٹر کے بارے میں کوئی معلومات نہیں ہوتی ہیں۔ یہ معلومات صرف تالیف کے دوران دستیاب ہوتی ہے اور رن ٹائم سے پہلے مٹ جاتی ہے (ناقابل رسائی ہو جاتی ہے)۔ اگر آپ اپنے میں غلط قسم کی آبجیکٹ ڈالنے کی کوشش کرتے ہیں List<String>تو کمپائلر ایک غلطی پیدا کرے گا۔ یہ بالکل وہی ہے جو زبان کے تخلیق کار اس وقت حاصل کرنا چاہتے ہیں جب انہوں نے generics: compile-time checks بنائے۔ لیکن جب آپ کا تمام جاوا کوڈ بائیک کوڈ میں بدل جاتا ہے، تو اس میں ٹائپ پیرامیٹرز کے بارے میں مزید معلومات نہیں ہوتی ہیں۔ بائیک کوڈ میں، آپ کی بلیوں کی فہرست تاروں 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ضروری ہے ۔ طریقہ کار میں ، ہم ایک بناتے ہیں ، یعنی قسم کی دلیل ٹائپ پیرامیٹر کی جگہ لے لیتی ہے۔ ہم ایک اور ایک کو بھی پاس کر رہے ہیں۔ طریقہ ۔ کیا آپ کو لگتا ہے کہ ہمارا پروگرام کام کرے گا؟ آخر کار، ہم نے قسم کی دلیل کے طور پر بیان کیا ہے، لیکن یقینی طور پر ایک کو کاسٹ نہیں کیا جا سکتا ! آئیے طریقہ کو چلائیں اور چیک کریں۔ کنسول آؤٹ پٹ: TTestClassmain()TestClass<Integer>IntegerIntegerDoubleStringcreateAndAdd2Values()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];
   }
}
لیکن ایسا کیوں کیا جاتا ہے؟ ایسی صفوں کی تخلیق کی اجازت کیوں نہیں ہے؟ یہ سب قسم کی حفاظت فراہم کرنے کے لیے ہے۔ اگر مرتب کرنے والا ہمیں عام اشیاء کی ایسی صفیں بنانے دیتا ہے، تو ہم اپنے لیے بہت ساری پریشانیاں پیدا کر سکتے ہیں۔ یہاں جوشوا بلوچ کی کتاب "Effective Java" سے ایک سادہ سی مثال ہے۔
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
لیکن یہاں ایک پہلو ہے جس کے بارے میں ہم نے بات نہیں کی۔ اوریکل دستاویزات میں، آپ دیکھیں گے کہ کلاس کلاس عام ہے! مٹانے کی قسم - 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# میں)۔ ویسے، ہم نے جنرک کا مطالعہ نہیں کیا ہے! اگلے سبق میں، آپ جنرک کی چند مزید خصوصیات سے واقف ہوں گے۔ ابھی کے لئے، یہ ایک دو کاموں کو حل کرنے کے لئے اچھا ہو گا! :)
تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION