JavaRush /جاوا بلاگ /Random-UR /جاوا میں جنرک کا نظریہ یا قوسین کو عملی طور پر کیسے لگایا...
Viacheslav
سطح

جاوا میں جنرک کا نظریہ یا قوسین کو عملی طور پر کیسے لگایا جائے۔

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

تعارف

JSE 5.0 سے شروع کرتے ہوئے، جاوا لینگویج کے ہتھیاروں میں جنرک شامل کیے گئے۔
جاوا میں جنرک کا نظریہ یا قوسین کو عملی طور پر کیسے لگایا جائے - 1

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

جنرکس (جنرلائزیشنز) عام پروگرامنگ کو لاگو کرنے کے لیے جاوا زبان کے خاص ذرائع ہیں: ڈیٹا اور الگورتھم کو بیان کرنے کا ایک خاص طریقہ جو آپ کو مختلف قسم کے ڈیٹا کے ساتھ ان کی تفصیل کو تبدیل کیے بغیر کام کرنے کی اجازت دیتا ہے۔ اوریکل کی ویب سائٹ پر، ایک علیحدہ ٹیوٹوریل جنرک کے لیے وقف ہے: " سبق: جنرکس

سب سے پہلے، جنرک کو سمجھنے کے لیے، آپ کو یہ سمجھنے کی ضرورت ہے کہ ان کی بالکل ضرورت کیوں ہے اور وہ کیا فراہم کرتے ہیں۔ سیکشن میں ٹیوٹوریل میں " جنرک کیوں استعمال کریں ؟" کہا جاتا ہے کہ مقاصد میں سے ایک مضبوط کمپائل ٹائم ٹائپ چیکنگ اور واضح کاسٹنگ کی ضرورت کو ختم کرنا ہے۔
جاوا میں جنرک کا نظریہ یا قوسین کو عملی طور پر کیسے لگایا جائے - 2
آئیے تجربات کے لیے اپنا پسندیدہ ٹیوٹوریل پوائنٹ آن لائن جاوا کمپائلر تیار کریں ۔ آئیے اس کوڈ کا تصور کریں:
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);
	}
}
یہ کوڈ ٹھیک چلے گا۔ لیکن کیا ہوگا اگر وہ ہمارے پاس آئے اور کہا کہ جملہ "ہیلو، دنیا!" مارا پیٹا اور آپ صرف ہیلو واپس کر سکتے ہیں؟ آئیے کوڈ سے سٹرنگ کے ساتھ کنکٹنیشن کو ہٹا دیں ", world!"۔ ایسا لگتا ہے کہ اس سے زیادہ بے ضرر اور کیا ہو سکتا ہے؟ لیکن درحقیقت، ہمیں تالیف کے دوران ایک ایرر موصول ہوگا : error: incompatible types: Object cannot be converted to String بات یہ ہے کہ ہمارے معاملے میں List آبجیکٹ کی قسم کی اشیاء کی فہرست کو محفوظ کرتی ہے۔ چونکہ 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);
		}
	}
}
تاہم، اس معاملے میں، کیونکہ فہرست اشیاء کی فہرست کو قبول کرتی ہے، یہ نہ صرف String بلکہ Integer کو بھی ذخیرہ کرتی ہے۔ لیکن سب سے بری بات یہ ہے کہ اس معاملے میں مرتب کرنے والے کو کچھ غلط نظر نہیں آئے گا۔ اور یہاں ہمیں ایگزیکیوشن کے دوران ایک ایرر موصول ہوگا (وہ یہ بھی کہتے ہیں کہ ایرر "رن ٹائم" پر موصول ہوا تھا)۔ غلطی یہ ہوگی: java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String متفق ہوں، سب سے زیادہ خوشگوار نہیں۔ اور یہ سب اس لیے ہے کہ کمپائلر مصنوعی ذہانت نہیں ہے اور وہ ہر چیز کا اندازہ نہیں لگا سکتا جس کا پروگرامر کا مطلب ہے۔ کمپائلر کو مزید بتانے کے لیے کہ ہم کن اقسام کو استعمال کرنے جا رہے ہیں، Java 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 کے اضافے کو نہیں ہٹاتے، کیونکہ یہ انٹیجر ہے۔ وہ ہمیں بتائے گا۔ بہت سے لوگ جنرک کو "نحوی شوگر" کہتے ہیں۔ اور وہ درست ہیں، کیونکہ جب generics مرتب کیا جائے گا تو واقعی وہی ذاتیں بن جائیں گی۔ آئیے مرتب شدہ کلاسوں کے بائیک کوڈ کو دیکھتے ہیں: دستی کاسٹنگ کے ساتھ اور جنرک استعمال کرتے ہوئے:
جاوا میں جنرک کا نظریہ یا قوسین کو عملی طور پر کیسے لگایا جائے - 3
تالیف کے بعد، generics کے بارے میں کسی بھی معلومات کو مٹا دیا جاتا ہے۔ اسے "Type Erasure" یا " Type Erasure " کہتے ہیں۔ ٹائپ ایریزر اور جنرکس کو JDK کے پرانے ورژنز کے ساتھ پسماندہ مطابقت فراہم کرنے کے لیے ڈیزائن کیا گیا ہے، جبکہ کمپائلر کو جاوا کے نئے ورژنز میں ٹائپ انفرنس میں مدد کرنے کی اجازت دیتا ہے۔
جاوا میں جنرک کا نظریہ یا قوسین کو عملی طور پر کیسے لگایا جائے - 4

خام قسمیں یا خام اقسام

generics کے بارے میں بات کرتے وقت، ہمارے پاس ہمیشہ دو قسمیں ہوتی ہیں: ٹائپ شدہ اقسام (Generic Types) اور "raw" اقسام (Raw Types)۔ خام قسمیں زاویہ بریکٹ میں "قابلیت" کی وضاحت کیے بغیر قسمیں ہیں:
جاوا میں جنرک کا نظریہ یا قوسین کو عملی طور پر کیسے لگایا جائے - 5
ٹائپ شدہ اقسام اس کے برعکس ہیں، "وضاحت" کے اشارے کے ساتھ:
جاوا میں جنرک کا نظریہ یا قوسین کو عملی طور پر کیسے لگایا جائے - 6
جیسا کہ ہم دیکھ سکتے ہیں، ہم نے ایک غیر معمولی ڈیزائن کا استعمال کیا، اسکرین شاٹ میں ایک تیر کے ساتھ نشان لگایا گیا ہے۔ یہ ایک خاص نحو ہے جسے جاوا 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") Suppress کا ترجمہ Suppress کے طور پر کیا جاتا ہے، یعنی لفظی طور پر، انتباہات کو دبانے کے لیے۔ لیکن اس کے بارے میں سوچیں کہ آپ نے اس کی نشاندہی کرنے کا فیصلہ کیوں کیا؟ قاعدہ نمبر ایک یاد رکھیں اور شاید آپ کو ٹائپنگ شامل کرنے کی ضرورت ہے۔
جاوا میں جنرک کا نظریہ یا قوسین کو عملی طور پر کیسے لگایا جائے - 7

عام طریقے

جنرک آپ کو طریقے ٹائپ کرنے کی اجازت دیتے ہیں۔ اوریکل ٹیوٹوریل میں اس خصوصیت کے لیے وقف ایک الگ سیکشن ہے: " Generic Methods "۔ اس ٹیوٹوریل سے، نحو کو یاد رکھنا ضروری ہے:
  • زاویہ بریکٹ کے اندر ٹائپ کردہ پیرامیٹرز کی فہرست شامل ہے۔
  • ٹائپ شدہ پیرامیٹرز کی فہرست واپس شدہ طریقہ سے پہلے جاتی ہے۔
آئیے ایک مثال دیکھتے ہیں:
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));
		}
    }
}
اگر آپ یوٹیل کلاس کو دیکھیں تو ہمیں اس میں دو ٹائپ شدہ طریقے نظر آتے ہیں۔ ٹائپ انفرنس کے ساتھ، ہم ٹائپ ڈیفینیشن براہ راست مرتب کرنے والے کو فراہم کر سکتے ہیں، یا ہم خود اس کی وضاحت کر سکتے ہیں۔ دونوں اختیارات مثال میں پیش کیے گئے ہیں۔ ویسے اگر آپ اس کے بارے میں سوچیں تو نحو کافی منطقی ہے۔ جب کوئی طریقہ ٹائپ کرتے ہیں، تو ہم طریقہ سے پہلے generic کی وضاحت کرتے ہیں کیونکہ اگر ہم طریقہ کے بعد generic استعمال کرتے ہیں، تو Java یہ معلوم نہیں کر پائے گا کہ کون سی قسم استعمال کرنی ہے۔ لہذا، ہم پہلے اعلان کرتے ہیں کہ ہم عام T استعمال کریں گے، اور پھر ہم کہتے ہیں کہ ہم اس عام کو واپس کرنے جا رہے ہیں۔ قدرتی طور پر، Util.<Integer>getValue(element, String.class)یہ ایک غلطی کے ساتھ ناکام ہو جائے گا incompatible types: Class<String> cannot be converted to 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);
		}
    }
}
یہ بہت اچھا کام کرے گا۔ لیکن صرف اس وقت تک جب تک کمپائلر یہ سمجھتا ہے کہ کہلائے گئے طریقہ میں انٹیجر کی قسم ہے۔ آئیے کنسول آؤٹ پٹ کو درج ذیل لائن سے بدلتے ہیں: System.out.println(Util.getValue(element) + 1); اور ہمیں خرابی ملتی ہے: بائنری آپریٹر '+' کے لیے خراب آپرینڈ کی قسمیں، پہلی قسم: آبجیکٹ، دوسری قسم: int یعنی اقسام کو مٹا دیا گیا ہے۔ کمپائلر دیکھتا ہے کہ کسی نے بھی قسم کی وضاحت نہیں کی ہے، قسم کو آبجیکٹ کے طور پر بیان کیا گیا ہے اور کوڈ پر عمل درآمد غلطی کے ساتھ ناکام ہو جاتا ہے۔
Теория дженериков в Java or How на практике ставить скобки - 8

عام اقسام

آپ نہ صرف طریقے ٹائپ کر سکتے ہیں بلکہ خود کلاسز بھی لکھ سکتے ہیں۔ اوریکل کے پاس ان کے گائیڈ میں اس کے لیے مخصوص " عام اقسام " سیکشن ہے۔ آئیے ایک مثال دیکھتے ہیں:
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);
}
یہ اچھا کام کرے گا۔ مرتب کرنے والا دیکھتا ہے کہ نمبروں کی ایک فہرست اور قسم String کا مجموعہ ہے۔ لیکن کیا ہوگا اگر ہم جنرک کو مٹا دیں اور یہ کریں:
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 erasure دوبارہ ٹائپ کریں۔ چونکہ کلاس میں اب کوئی عام نہیں ہے، اس لیے مرتب کرنے والا فیصلہ کرتا ہے کہ چونکہ ہم نے ایک فہرست پاس کی ہے، اس لیے 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> { جنرکس میں وائلڈ کارڈ کا تصور بھی ہے https://docs.oracle.com/javase/tutorial/java/generics/wildcards.html وہ، بدلے میں، تین اقسام میں تقسیم ہوتے ہیں: نام نہاد Get Put اصول وائلڈ کارڈز پر لاگو ہوتا ہے ۔ ان کا اظہار مندرجہ ذیل شکل میں کیا جا سکتا ہے۔
Теория дженериков в Java or How на практике ставить скобки - 9
اس اصول کو PECS (Producer Extends Consumer Super) اصول بھی کہا جاتا ہے۔ آپ Habré پر مضمون " جاوا API کے استعمال کو بہتر بنانے کے لیے عام وائلڈ کارڈز کا استعمال " کے ساتھ ساتھ اسٹیک اوور فلو پر بہترین بحث میں مزید پڑھ سکتے ہیں: " جنرک جاوا میں وائلڈ کارڈز کا استعمال "۔ یہاں جاوا سورس سے ایک چھوٹی سی مثال ہے - Collections.copy طریقہ:
Теория дженериков в Java or How на практике ставить скобки - 10
ٹھیک ہے، یہ کیسے کام نہیں کرے گا اس کی ایک چھوٹی سی مثال:
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);
}
لیکن اگر آپ توسیع کو سپر سے تبدیل کرتے ہیں تو سب کچھ ٹھیک ہو جائے گا۔ چونکہ ہم فہرست کو آؤٹ پٹ کرنے سے پہلے ایک قدر سے بھرتے ہیں، اس لیے یہ ہمارے لیے صارف ہے، یعنی صارف۔ لہذا، ہم سپر استعمال کرتے ہیں.

وراثت

generics کی ایک اور غیر معمولی خصوصیت ہے - ان کی وراثت. جنرکس کی وراثت کو اوریکل ٹیوٹوریل میں سیکشن " جنرکس، وراثت، اور ذیلی قسمیں " میں بیان کیا گیا ہے۔ اہم بات یہ ہے کہ درج ذیل کو یاد رکھیں اور ان کا احساس کریں۔ ہم یہ نہیں کر سکتے:
List<CharSequence> list1 = new ArrayList<String>();
کیونکہ وراثت عام کے ساتھ مختلف طریقے سے کام کرتی ہے:
Теория дженериков в Java or How на практике ставить скобки - 11
اور یہاں ایک اور اچھی مثال ہے جو غلطی کے ساتھ ناکام ہو جائے گی:
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