CodeGym /جاوا بلاگ /Random-UR /جاوا جنرکس: عملی طور پر زاویہ بریکٹ کا استعمال کیسے کریں۔...
John Squirrels
سطح
San Francisco

جاوا جنرکس: عملی طور پر زاویہ بریکٹ کا استعمال کیسے کریں۔

گروپ میں شائع ہوا۔

تعارف

JSE 5.0 سے شروع کرتے ہوئے، جاوا زبان کے ہتھیاروں میں جنرک شامل کیے گئے۔

جاوا میں عام کیا ہیں؟

جنرک پروگرامنگ کو لاگو کرنے کے لیے جاوا کا خاص طریقہ کار ہے — ڈیٹا اور الگورتھم کو بیان کرنے کا ایک طریقہ جو آپ کو الگورتھم کی تفصیل کو تبدیل کیے بغیر مختلف ڈیٹا ٹائپس کے ساتھ کام کرنے دیتا ہے۔ اوریکل ویب سائٹ پر ایک الگ سبق ہے جو عامات کے لیے وقف ہے: " سبق "۔ عمومیات کو سمجھنے کے لیے، آپ کو پہلے یہ معلوم کرنا ہوگا کہ ان کی ضرورت کیوں ہے اور وہ کیا دیتے ہیں۔ ٹیوٹوریل کا " جینرک کیوں استعمال کریں؟ " سیکشن کہتا ہے کہ ایک دو مقاصد کمپائل کے وقت مضبوط قسم کی جانچ اور واضح ذاتوں کی ضرورت کو ختم کرنا ہیں۔ جاوا میں جنرکس: عملی طور پر زاویہ بریکٹ کا استعمال کیسے کریں - 1آئیے اپنے پیارے ٹیوٹوریل پوائنٹ آن لائن جاوا کمپائلر میں کچھ ٹیسٹوں کی تیاری کریں۔ فرض کریں کہ آپ کے پاس درج ذیل کوڈ ہے:
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);
	}
}
یہ کوڈ بالکل ٹھیک چلے گا۔ لیکن کیا ہوگا اگر باس ہمارے پاس آئے اور کہے کہ "ہیلو، دنیا!" ایک زیادہ استعمال شدہ جملہ ہے اور یہ کہ آپ کو صرف "ہیلو" واپس کرنا ہوگا؟ ہم اس کوڈ کو ہٹا دیں گے جو "، دنیا!" یہ کافی بے ضرر لگتا ہے، ٹھیک ہے؟ لیکن ہمیں اصل میں COMPILE TIME پر ایک غلطی ملتی ہے:

error: incompatible types: Object cannot be converted to String
مسئلہ یہ ہے کہ ہماری فہرست میں آبجیکٹ کو اسٹور کیا جاتا ہے۔ سٹرنگ آبجیکٹ کی اولاد ہے (چونکہ تمام جاوا کلاسز واضح طور پر آبجیکٹ کی وارث ہوتی ہیں )، جس کا مطلب ہے کہ ہمیں ایک واضح کاسٹ کی ضرورت ہے، لیکن ہم نے اسے شامل نہیں کیا۔ کنکٹنیشن آپریشن کے دوران، static 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);
		}
	}
}
تاہم، اس معاملے میں، چونکہ فہرست اشیاء کو لیتی ہے، اس لیے یہ نہ صرف String s، بلکہ Integer s کو بھی ذخیرہ کر سکتی ہے۔ لیکن سب سے بری بات یہ ہے کہ مرتب کرنے والے کو یہاں کچھ غلط نظر نہیں آتا۔ اور اب ہمیں رن ٹائم پر ایک ایرر ملے گا (جسے "رن ٹائم ایرر" کہا جاتا ہے)۔ غلطی یہ ہوگی:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
آپ کو اتفاق کرنا ہوگا کہ یہ بہت اچھا نہیں ہے۔ اور یہ سب اس لیے کہ کمپائلر کوئی مصنوعی ذہانت نہیں ہے جو ہمیشہ پروگرامر کے ارادے کا صحیح اندازہ لگا سکے۔ جاوا SE 5 نے generics متعارف کرایا تاکہ ہم مرتب کرنے والے کو اپنے ارادوں کے بارے میں بتا سکیں - جس کے بارے میں ہم استعمال کرنے جا رہے ہیں۔ ہم کمپائلر کو یہ بتا کر اپنا کوڈ ٹھیک کرتے ہیں کہ ہم کیا چاہتے ہیں:
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 کا اضافہ کرنے والی لائن کو ہٹا نہیں دیں گے، کیونکہ یہ ایک Integer ہے ۔ اور یہ ہمیں بتائے گا۔ بہت سے لوگ جنرک کو "نحوی شوگر" کہتے ہیں۔ اور وہ ٹھیک کہتے ہیں، چونکہ جنرک کے مرتب ہونے کے بعد، وہ واقعی ایک ہی قسم کے تبادلوں بن جاتے ہیں۔ آئیے مرتب شدہ کلاسوں کے بائیک کوڈ کو دیکھیں: ایک جو واضح کاسٹ استعمال کرتا ہے اور ایک جو جنرک استعمال کرتا ہے: جاوا میں جنرکس: عملی طور پر زاویہ بریکٹ کا استعمال کیسے کریں - 2تالیف کے بعد، تمام جنرکس مٹ جاتی ہیں۔ اسے " ٹائپ ایریزر " کہتے ہیں۔ ٹائپ ایریزر اور جنرکس کو JDK کے پرانے ورژنز کے ساتھ پسماندہ مطابقت رکھنے کے لیے ڈیزائن کیا گیا ہے جبکہ ساتھ ہی ساتھ کمپائلر کو جاوا کے نئے ورژن میں ٹائپ تعریفوں میں مدد کرنے کی اجازت دیتا ہے۔

کچی اقسام

جنرک کی بات کرتے ہوئے، ہمارے پاس ہمیشہ دو قسمیں ہوتی ہیں: پیرامیٹرائزڈ اقسام اور خام اقسام۔ خام قسمیں وہ قسمیں ہیں جو زاویہ بریکٹ میں "قسم کی وضاحت" کو چھوڑ دیتی ہیں: جاوا میں جنرکس: عملی طور پر زاویہ بریکٹ کا استعمال کیسے کریں - 3پیرامیٹرائزڈ اقسام میں، ہاتھ پر، ایک "وضاحت" شامل ہے: جاوا میں جنرکس: عملی طور پر زاویہ بریکٹ کا استعمال کیسے کریں - 4جیسا کہ آپ دیکھ سکتے ہیں، ہم نے ایک غیر معمولی تعمیر کا استعمال کیا، جس پر اسکرین شاٹ میں تیر کا نشان لگایا گیا ہے۔ یہ ایک خاص نحو ہے جسے جاوا 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(عنصر، 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.
دوسرے لفظوں میں، ٹائپ ایریزر واقع ہو گیا ہے۔ کمپائلر دیکھتا ہے کہ کسی نے بھی قسم کی وضاحت نہیں کی ہے، لہذا قسم کو آبجیکٹ کے طور پر اشارہ کیا گیا ہے اور طریقہ غلطی کے ساتھ ناکام ہوجاتا ہے۔

عام کلاسز

نہ صرف طریقوں کو پیرامیٹرائز کیا جاسکتا ہے۔ کلاسز بھی کر سکتے ہیں۔ اوریکل کے ٹیوٹوریل کا "Generic Types" سیکشن اس کے لیے وقف ہے۔ آئیے ایک مثال پر غور کریں:
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> {
جنرک بھی وائلڈ کارڈز کی حمایت کرتے ہیں وہ تین اقسام میں تقسیم ہیں: آپ کے وائلڈ کارڈ کا استعمال Get-Put کے اصول پر عمل پیرا ہونا چاہیے ۔ اس کا اظہار اس طرح کیا جا سکتا ہے:
  • ایکسٹینڈ وائلڈ کارڈ استعمال کریں جب آپ کو صرف ایک ڈھانچہ سے باہر کی قدر مل جاتی ہے۔
  • ایک سپر وائلڈ کارڈ استعمال کریں جب آپ صرف اقدار کو ڈھانچے میں ڈالیں۔
  • اور وائلڈ کارڈ کا استعمال نہ کریں جب آپ دونوں کسی ڈھانچے کو حاصل کرنا اور ڈالنا چاہتے ہیں۔
اس اصول کو Producer Extends Consumer Super (PECS) اصول بھی کہا جاتا ہے۔ یہاں جاوا کے Collections.copy طریقہ کار کے سورس کوڈ سے ایک چھوٹی سی مثال ہے : جاوا میں جنرک: عملی طور پر زاویہ بریکٹ کا استعمال کیسے کریں - 5اور یہاں اس کی ایک چھوٹی سی مثال ہے جو کام نہیں کرے گا:
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);
}
لیکن اگر آپ extensions کو super سے بدل دیں تو سب کچھ ٹھیک ہے۔ چونکہ ہم فہرست کو اس کے مواد کو ظاہر کرنے سے پہلے ایک قدر کے ساتھ آباد کرتے ہیں، یہ صارف ہے ۔ اس کے مطابق، ہم سپر استعمال کرتے ہیں.

وراثت

جنرک کی ایک اور دلچسپ خصوصیت ہے: وراثت۔ جنرکس کے لیے وراثت کے کام کرنے کا طریقہ Oracle کے ٹیوٹوریل میں " Generics, Inheritance, and Subtypes " کے تحت بیان کیا گیا ہے۔ اہم بات یہ ہے کہ درج ذیل کو یاد رکھیں اور پہچانیں۔ ہم یہ نہیں کر سکتے:
List<CharSequence> list1 = new ArrayList<String>();
کیونکہ وراثت عام کے ساتھ مختلف طریقے سے کام کرتی ہے: جاوا میں جنرک: عملی طور پر زاویہ بریکٹ کا استعمال کیسے کریں - 6اور یہاں ایک اور اچھی مثال ہے جو غلطی کے ساتھ ناکام ہو جائے گی۔
List<String> list1 = new ArrayList<>();
List<Object> list2 = list1;
ایک بار پھر، یہاں سب کچھ آسان ہے. List<String> List<Object> کی اولاد نہیں ہے ، حالانکہ String آبجیکٹ کی اولاد ہے ۔ جو کچھ آپ نے سیکھا ہے اس کو تقویت دینے کے لیے، ہم تجویز کرتے ہیں کہ آپ ہمارے جاوا کورس سے ایک ویڈیو سبق دیکھیں
لہذا ہم نے جنرک سے متعلق اپنی یادداشت کو تازہ کر دیا ہے۔ اگر آپ شاذ و نادر ہی ان کی صلاحیتوں سے بھرپور فائدہ اٹھاتے ہیں، تو کچھ تفصیلات مبہم ہو جاتی ہیں۔ مجھے امید ہے کہ اس مختصر جائزے نے آپ کی یادداشت کو تیز کرنے میں مدد کی ہے۔ اس سے بھی بہتر نتائج کے لیے، میں سختی سے مشورہ دیتا ہوں کہ آپ مندرجہ ذیل مواد سے واقف ہوں:
تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION