हाय! आम्ही आमची जेनेरिकवरील धड्यांची मालिका सुरू ठेवतो. ते काय आहेत आणि त्यांची आवश्यकता का आहे याची आम्हाला पूर्वी सामान्य कल्पना मिळाली. आज आपण जेनेरिकच्या काही वैशिष्ट्यांबद्दल आणि त्यांच्यासोबत काम करण्याबद्दल अधिक जाणून घेऊ. चल जाऊया! शेवटच्या धड्यात
, आम्ही जेनेरिक प्रकार आणि कच्चे प्रकार यांच्यातील फरकाबद्दल बोललो . कच्चा प्रकार हा एक सामान्य वर्ग आहे ज्याचा प्रकार काढला गेला आहे.
दस्तऐवजीकरण म्हणते, "T - या वर्ग ऑब्जेक्टद्वारे मॉडेल केलेल्या वर्गाचा प्रकार." हे दस्तऐवजीकरणाच्या भाषेतून साध्या भाषणात भाषांतरित करताना, आपल्याला समजते की ऑब्जेक्टचा वर्ग

List list = new ArrayList();
येथे एक उदाहरण आहे. आमच्या मध्ये कोणत्या प्रकारच्या वस्तू ठेवल्या जातील हे आम्ही येथे सूचित करत नाही List
. जर आम्ही असे तयार करण्याचा प्रयत्न केला List
आणि त्यात काही वस्तू जोडल्या, तर आम्हाला IDEA मध्ये एक चेतावणी दिसेल:
"Unchecked call to add(E) as a member of raw type of java.util.List".
परंतु आम्ही या वस्तुस्थितीबद्दल देखील बोललो की जेनेरिक्स फक्त जावा 5 मध्ये दिसून आले. ही आवृत्ती रिलीज होईपर्यंत, प्रोग्रामरने आधीच कच्चे प्रकार वापरून कोडचा एक समूह लिहिला होता, त्यामुळे भाषेचे हे वैशिष्ट्य कार्य करणे थांबवू शकले नाही आणि क्षमता Java मध्ये रॉ प्रकार तयार करणे जतन केले गेले. तथापि, समस्या अधिक व्यापक असल्याचे दिसून आले. तुम्हाला माहिती आहे की, जावा कोड बायटेकोड नावाच्या एका विशेष संकलित स्वरूपात रूपांतरित केला जातो, जो नंतर Java आभासी मशीनद्वारे कार्यान्वित केला जातो. पण जर आपण रूपांतरण प्रक्रियेदरम्यान बाईटकोडमध्ये टाइप पॅरामीटर्सची माहिती ठेवली तर ते आधी लिहिलेले सर्व कोड खंडित करेल, कारण Java 5 पूर्वी कोणतेही टाइप पॅरामीटर्स नव्हते! जेनेरिकसह काम करताना, एक अतिशय महत्त्वाची संकल्पना आहे जी तुम्हाला लक्षात ठेवण्याची आवश्यकता आहे. त्याला टाइप इरेजर म्हणतात. याचा अर्थ क्लासमध्ये प्रकार पॅरामीटरबद्दल कोणतीही माहिती नसते. ही माहिती केवळ संकलनादरम्यान उपलब्ध असते आणि रनटाइमपूर्वी मिटवली जाते (अगम्य होते). तुम्ही तुमच्या मध्ये चुकीच्या प्रकारचा ऑब्जेक्ट ठेवण्याचा प्रयत्न केल्यास List<String>
, कंपाइलर एक त्रुटी निर्माण करेल. जेनेरिक: कंपाइल-टाइम चेक तयार केल्यावर भाषेच्या निर्मात्यांना हेच साध्य करायचे आहे. परंतु जेव्हा तुमचा सर्व Java कोड बाइटकोडमध्ये बदलतो, तेव्हा त्यात टाइप पॅरामीटर्सबद्दल माहिती नसते. बायकोडमध्ये, तुमची 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
प्रकार पॅरामीटरच्या जागी बदलतो. आम्ही a Double
आणि a String
to देखील पास करत आहोत. पद्धत createAndAdd2Values()
. तुम्हाला असे वाटते की आमचा प्रोग्राम कार्य करेल? शेवटी, आम्ही Integer
प्रकार युक्तिवाद म्हणून निर्दिष्ट केले आहे, परंतु String
निश्चितपणे एखाद्याला कास्ट करता येणार नाही Integer
! चला चालवूयाmain()
पद्धत आणि तपासणी. कन्सोल आउटपुट:
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
अॅरे आणि जेनेरिकमध्ये एवढा मोठा फरक असल्यामुळे त्यांच्यात सुसंगतता समस्या असू शकतात. सर्वात महत्त्वाचे म्हणजे, तुम्ही जेनेरिक वस्तूंचा अॅरे किंवा अगदी पॅरामीटराइज्ड अॅरे तयार करू शकत नाही. ते थोडे गोंधळात टाकणारे वाटते का? चला पाहुया. उदाहरणार्थ, तुम्ही Java मध्ये यापैकी काहीही करू शकत नाही:
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 सह बदलतो . अशा प्रकारे, आम्ही एका अॅरेमध्ये ठेवतो जो फक्त संग्रहित करण्याच्या उद्देशाने होताX
X
Object
objects()
List<String>
List<Integer>
List<Integer>
List<String>
वस्तू! जेव्हा आम्ही ओळ 5 कार्यान्वित करतो तेव्हाच आम्हाला त्रुटी आढळेल. ClassCastException
रनटाइमच्या वेळी A फेकले जाईल. त्यानुसार, जावामध्ये अशा अॅरेच्या निर्मितीवर बंदी घालण्यात आली. हे आपल्याला अशा परिस्थिती टाळू देते.
मी टाइप इरेजर कसे मिळवू शकतो?
बरं, आम्ही टाइप इरेजरबद्दल शिकलो. चला सिस्टमला फसवण्याचा प्रयत्न करूया! :) कार्य: आमच्याकडे एक सामान्यTestClass<T>
वर्ग आहे. createNewT()
आम्हाला या वर्गासाठी एक पद्धत लिहायची आहे जी एक नवीन T
ऑब्जेक्ट तयार करेल आणि परत करेल. पण हे अशक्य आहे, बरोबर? संकलित करताना प्रकाराबद्दलची सर्व माहिती T
पुसून टाकली जाते आणि रनटाइममध्ये आपल्याला कोणत्या प्रकारची ऑब्जेक्ट तयार करायची आहे हे ठरवता येत नाही. प्रत्यक्षात हे करण्याचा एक अवघड मार्ग आहे. तुम्हाला कदाचित आठवत असेल की Java चा 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
परंतु येथे एक पैलू आहे ज्याबद्दल आपण बोललो नाही. ओरॅकल डॉक्युमेंटेशनमध्ये, तुम्हाला दिसेल की क्लास क्लास जेनेरिक आहे! 
https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html
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
वस्तु :) त्याबरोबर आजचा धडा संपला. जेनेरिकसह काम करताना तुम्ही नेहमी टाइप इरेजर लक्षात ठेवावे. हे वर्कअराउंड फार सोयीस्कर वाटत नाही, परंतु तुम्ही हे समजून घेतले पाहिजे की जेनेरिक हे Java भाषेचा भाग नव्हते जेव्हा ते तयार केले गेले होते. हे वैशिष्ट्य, जे आम्हाला पॅरामीटराइज्ड कलेक्शन तयार करण्यात आणि संकलनादरम्यान त्रुटी पकडण्यात मदत करते, नंतर हाताळले गेले. पहिल्या आवृत्तीतील जेनेरिक समाविष्ट असलेल्या इतर काही भाषांमध्ये, कोणतेही प्रकार इरेजर नाही (उदाहरणार्थ, C# मध्ये). तसे, आम्ही जेनेरिकचा अभ्यास केला नाही! पुढील धड्यात, आपण जेनेरिकच्या आणखी काही वैशिष्ट्यांशी परिचित व्हाल. सध्या, दोन कार्ये सोडवणे चांगले होईल! :)
GO TO FULL VERSION