मैंने कभी-कभी सुना है कि जेनेरिक के साथ, जावा इसे सही नहीं मिला। (निकटतम संदर्भ, यहां )
मेरी अनुभवहीनता को क्षमा करें, लेकिन क्या उन्हें बेहतर बना सकता है?
मैंने कभी-कभी सुना है कि जेनेरिक के साथ, जावा इसे सही नहीं मिला। (निकटतम संदर्भ, यहां )
मेरी अनुभवहीनता को क्षमा करें, लेकिन क्या उन्हें बेहतर बना सकता है?
जवाबों:
खराब:
List<byte>
वास्तव में byte[]
उदाहरण के लिए समर्थित है , और कोई बॉक्सिंग की आवश्यकता नहीं है)अच्छा:
सबसे बड़ी समस्या यह है कि जावा जेनरिक केवल एक संकलन-समय है, और आप इसे रन-टाइम पर हटा सकते हैं। C # की प्रशंसा की जाती है क्योंकि यह अधिक रन-टाइम जाँच करता है। इस पोस्ट में वास्तव में कुछ अच्छी चर्चा है , और यह अन्य चर्चाओं से जुड़ा हुआ है।
Class
ऑब्जेक्ट्स को पास कर सकते हैं ।
मुख्य समस्या यह है कि जावा वास्तव में रनटाइम पर जेनरिक नहीं है। यह एक संकलन समय की सुविधा है।
जब आप जावा में एक सामान्य वर्ग बनाते हैं, तो वे "टाइप एरासुरे" नामक एक विधि का उपयोग करते हैं, जो वास्तव में सभी सामान्य प्रकारों को कक्षा से हटा देते हैं और अनिवार्य रूप से उन्हें ऑब्जेक्ट के साथ बदल देते हैं। जेनरिक का एक मील ऊंचा संस्करण यह है कि कंपाइलर केवल निर्दिष्ट जेनेरिक प्रकार में कास्ट सम्मिलित करता है जब भी यह विधि बॉडी में प्रकट होता है।
इसमें कई डाउनसाइड हैं। एक सबसे बड़ा, IMHO, यह है कि आप एक सामान्य प्रकार का निरीक्षण करने के लिए प्रतिबिंब का उपयोग नहीं कर सकते हैं। प्रकार वास्तव में बाइट कोड में सामान्य नहीं होते हैं और इसलिए जेनरिक के रूप में निरीक्षण नहीं किया जा सकता है।
यहाँ मतभेदों के महान अवलोकन: http://www.jprl.com/Blog/archive/development/2007/Aug-31.html
(१) कुछ बहुत ही अजीब व्यवहार होता है। सबसे अच्छा उदाहरण मैं सोच सकता हूं। मान लीजिये:
public class MyClass<T> {
T getStuff() { ... }
List<String> getOtherStuff() { ... }
}
फिर दो चर घोषित करें:
MyClass<T> m1 = ...
MyClass m2 = ...
अब कॉल करें getOtherStuff()
:
List<String> list1 = m1.getOtherStuff();
List<String> list2 = m2.getOtherStuff();
दूसरे में इसके जेनेरिक प्रकार के तर्क को संकलक द्वारा छीन लिया गया है क्योंकि यह एक कच्चा प्रकार है (जिसका अर्थ है कि पैरामीटर प्रकार की आपूर्ति नहीं की गई है) भले ही इसके पास कुछ भी न हो पैरामीटर प्रकार के साथ देना है।
मैं JDK से अपनी पसंदीदा घोषणा का भी उल्लेख करूंगा:
public class Enum<T extends Enum<T>>
वाइल्डकार्डिंग (जो एक मिश्रित बैग है) के अलावा मुझे लगता है कि। नेट जेनरिक बेहतर हैं।
public class Redundancy<R extends Redundancy<R>>
;)
The expression of type List needs unchecked conversion to conform to List<String>
Enum<T extends Enum<T>>
पहली बार में अजीब / निरर्थक लग सकता है, लेकिन वास्तव में बहुत दिलचस्प है, कम से कम जावा के बाधाओं के भीतर / यह सामान्य है। Enums में एक स्थिर values()
विधि है, जो अपने तत्वों की एक सरणी को enum के रूप में टाइप करती है, नहीं Enum
, और यह प्रकार जेनेरिक पैरामीटर द्वारा निर्धारित किया जाता है, जिसका अर्थ है कि आप चाहते हैं Enum<T>
। बेशक, वह टाइपिंग केवल एक एन्यूमरेटेड टाइप के संदर्भ में समझ में आता है, और सभी एनीमे उप-वर्ग होते हैं Enum
, इस प्रकार वे चाहते हैं Enum<T extends Enum>
। हालाँकि, जावा को जेनेरिक के साथ कच्चे प्रकार का मिश्रण पसंद नहीं है, इस प्रकार Enum<T extends Enum<T>>
स्थिरता के लिए।
मैं एक बहुत विवादास्पद राय बाहर फेंकने जा रहा हूँ। जेनरिक भाषा को जटिल बनाते हैं और कोड को जटिल करते हैं। उदाहरण के लिए, मान लें कि मेरे पास एक नक्शा है जो स्ट्रिंग की सूची में एक स्ट्रिंग को मैप करता है। पुराने दिनों में, मैं इसे बस के रूप में घोषित कर सकता था
Map someMap;
अब, मुझे इसे घोषित करना होगा
Map<String, List<String>> someMap;
और हर बार जब मैं इसे किसी विधि में पास करता हूं, मुझे उस बड़ी लंबी घोषणा को फिर से दोहराना होगा। मेरी राय में, यह सब अतिरिक्त टाइपिंग डेवलपर को विचलित करता है और उसे "ज़ोन" से बाहर ले जाता है। इसके अलावा, जब कोड बहुत सारे cruft से भर जाता है, तो कभी-कभी बाद में वापस आना मुश्किल होता है और महत्वपूर्ण तर्क खोजने के लिए जल्दी से सभी cruft के माध्यम से झारना।
जावा में पहले से ही सामान्य उपयोग में सबसे अधिक मौखिक भाषाओं में से एक होने के लिए एक खराब प्रतिष्ठा है, और जेनेरिक बस उस समस्या को जोड़ते हैं।
और क्या आप वास्तव में उस सभी अतिरिक्त वाचालता के लिए खरीदते हैं? कितनी बार आपको वास्तव में समस्याएँ हुईं जहाँ किसी ने एक इंटेगर को एक ऐसे संग्रह में डाल दिया, जिसे स्ट्रिंग्स को धारण करना चाहिए, या जहाँ किसी ने स्ट्रिंगर्स को इंटेगर के संग्रह से बाहर निकालने की कोशिश की? वाणिज्यिक जावा अनुप्रयोगों के निर्माण में काम करने के मेरे 10 वर्षों के अनुभव में, यह कभी भी त्रुटियों का एक बड़ा स्रोत नहीं रहा है। इसलिए, मुझे वास्तव में यकीन नहीं है कि आपको अतिरिक्त क्रियाशीलता के लिए क्या मिल रहा है। यह वास्तव में मुझे केवल अतिरिक्त नौकरशाही सामान के रूप में प्रभावित करता है।
अब मैं वास्तव में विवादास्पद होने जा रहा हूं। मुझे जावा 1.4 में संग्रह के साथ सबसे बड़ी समस्या के रूप में जो दिखता है वह हर जगह टाइपकास्ट करने के लिए आवश्यक है। मैं उन टाइपकास्ट को अतिरिक्त के रूप में देखता हूं, वर्बोज़ क्रॉफ्ट जो कि जेनरिक के समान ही कई समस्याएं हैं। इसलिए, उदाहरण के लिए, मैं बस नहीं कर सकता
List someList = someMap.get("some key");
मुझे करना है
List someList = (List) someMap.get("some key");
कारण, निश्चित रूप से, वह है () एक वस्तु लौटाता है जो सूची का एक सुपरस्क्रिप्ट है। इसलिए टाइपकास्ट के बिना असाइनमेंट नहीं बनाया जा सकता है। फिर, इस बारे में सोचें कि वह नियम आपको कितना खरीदता है। मेरे अनुभव से, ज्यादा नहीं।
मुझे लगता है कि जावा 1 से बेहतर होता अगर 1) इसमें जेनरिक नहीं जोड़ा गया होता, लेकिन 2) इसके बजाय एक सुपरटेप से एक उपप्रकार तक अंतर्निहित कास्टिंग की अनुमति दी थी। गलत जाति को रनटाइम पर पकड़ा जाए। तब मुझे परिभाषित करने की सरलता हो सकती थी
Map someMap;
और बाद में कर रहे हैं
List someList = someMap.get("some key");
सभी cruft चला जाएगा, और मुझे नहीं लगता कि मैं अपने कोड में बग का एक बड़ा नया स्रोत पेश करूंगा।
उन्हें संकलित करने और समय नहीं चलाने का एक और दुष्प्रभाव यह है कि आप सामान्य प्रकार के निर्माता को नहीं बुला सकते। तो आप उन्हें एक सामान्य कारखाने को लागू करने के लिए उपयोग नहीं कर सकते हैं ...
public class MyClass {
public T getStuff() {
return new T();
}
}
--jeffk ++
जावा जेनरिक को संकलन समय पर शुद्धता के लिए जांचा जाता है और फिर सभी प्रकार की जानकारी को हटा दिया जाता है (प्रक्रिया को इरेज़र कहा जाता है । इस प्रकार, जेनेरिक List<Integer>
अपने कच्चे प्रकार , गैर-जेनेरिक में कम हो जाएगाList
, जिसमें मनमाना वर्ग की वस्तुएँ हो सकती हैं।
यह रनटाइम में सूची में मनमानी वस्तुओं को सम्मिलित करने में सक्षम होता है, साथ ही अब यह बताना असंभव है कि सामान्य पैरामीटर के रूप में किस प्रकार का उपयोग किया गया था। उत्तरार्द्ध बदले में परिणाम देता है
ArrayList<Integer> li = new ArrayList<Integer>();
ArrayList<Float> lf = new ArrayList<Float>();
if(li.getClass() == lf.getClass()) // evaluates to true
System.out.println("Equal");
काश यह एक विकी होता तो मैं अन्य लोगों को जोड़ सकता था ... लेकिन ...
समस्या:
<
? MyObject का विस्तार करता है>
[], लेकिन मुझे इसकी अनुमति नहीं है)पूरे प्रकार को नष्ट करने की गड़बड़ी को अनदेखा करना, जैसा कि निर्दिष्ट है, जेनेरिक बस काम नहीं करते हैं।
यह संकलन:
List<Integer> x = Collections.emptyList();
लेकिन यह एक सिंटैक्स त्रुटि है:
foo(Collections.emptyList());
जहां फू को परिभाषित किया गया है:
void foo(List<Integer> x) { /* method body not important */ }
तो क्या एक अभिव्यक्ति प्रकार की जाँच इस बात पर निर्भर करती है कि क्या यह एक स्थानीय चर को सौंपा जा रहा है या विधि कॉल के वास्तविक पैरामीटर को। कितना पागल है?
जावा में जेनरिक का परिचय एक कठिन कार्य था क्योंकि आर्किटेक्ट विरासत कोड के साथ कार्यक्षमता, उपयोग में आसानी, और पिछड़े संगतता को संतुलित करने की कोशिश कर रहे थे। काफी उम्मीद है, समझौता करना पड़ा।
कुछ ऐसे भी हैं जो यह महसूस करते हैं कि जावा के जेनेरिक कार्यान्वयन ने भाषा की जटिलता को अस्वीकार्य स्तर तक बढ़ा दिया (देखें केन अर्नोल्ड की " जेनरिक कंसिस्टर्ड हार्मफुल ")। एंजेलिका लैंगर की जेनरिक एफएक्यू बहुत जटिल चीजें कैसे बन सकती हैं, इसके बारे में एक अच्छा विचार देता है।
जावा केवल समय पर जेनरिक लागू नहीं करता है, केवल संकलन समय पर।
इसका मतलब है कि आप दिलचस्प प्रकार कर सकते हैं जैसे गलत प्रकार को जेनेरिक कलेक्शंस में जोड़ना।
जावा जेनरिक केवल संकलन-समय हैं और गैर-जेनेरिक कोड में संकलित हैं। C # में, वास्तविक संकलित MSIL सामान्य है। प्रदर्शन के लिए इसके बहुत बड़े निहितार्थ हैं क्योंकि जावा अभी भी रनटाइम के दौरान आता है। अधिक के लिए यहाँ देखें ।
यदि आप जावा पॉस # 279 - जो डार्सी और एलेक्स बकले के साथ साक्षात्कार सुनते हैं , तो वे इस मुद्दे पर बात करते हैं। यह भी कहते हैं कि जावा के लिए संशोधित जेनरिक शीर्षक से एक नील Gafter ब्लॉग पोस्ट के लिए लिंक :
जावा में जिस तरह से जेनरिक को लागू किया गया है, उससे प्रतिबंध से कई लोग असंतुष्ट हैं। विशेष रूप से, वे इस बात से नाखुश हैं कि सामान्य प्रकार के पैरामीटर को पुन: लागू नहीं किया गया है: वे रनटाइम पर उपलब्ध नहीं हैं। जेनरिक को इरेज़र का उपयोग करके लागू किया जाता है, जिसमें सामान्य प्रकार के मापदंडों को केवल रनटाइम पर हटा दिया जाता है।
वह ब्लॉग पोस्ट, एक पुरानी प्रविष्टि का संदर्भ देता है, पज़लिंग थ्रू एरासुरे: उत्तर अनुभाग , जिसने आवश्यकताओं में माइग्रेशन संगतता के बारे में बात पर जोर दिया ।
लक्ष्य स्रोत और ऑब्जेक्ट कोड दोनों की पश्चगामी संगतता प्रदान करना था, और माइग्रेशन संगतता भी।