2011-09-28 12 views
7

यह मेरे पिछले प्रश्न का अनुवर्ती है लेकिन चूंकि पिछला धागा लंबा था, इसलिए मैंने लगभग एक ही विषय से संबंधित एक और धागा शुरू करने का फैसला किया।जावा जेनरिक: जेनेरिक विधियों का उपयोग करके प्रकार कैप्चर और उत्पन्न अनुमान के बारे में प्रश्न

public class GenericMethodInference { 

static <T> void test1(T t1, T t2) {} 
static <T> void test3(T t1, List <T> t2) {} 
static <T> void test4(List <T> t1, List <T> t2) {} 

public static void main(String [] args) { 

    List <Object> c = new LinkedList<Object>(); 
    List <? extends Object> d = new ArrayList<Integer>(); 
    List e = new ArrayList<Integer>(); 

    test1("Hello", new Integer(1)); // ok clause (1) 
    GenericMethodInference.<Object>test1("Hello", new Integer(1)); // ok clause (2) 
    test3("Hello", c); // ok clause (3) 
    test4(d,d) // clause (4) Error due to different type capture generated 

} 

नोट: यदि आप प्रत्येक खंड पर अपने कर्सर ले जाते हैं, आप अनुमान उत्पन्न होती है और ग्रहण पर प्रदर्शित किया जा रहा देखेंगे:

एक। खंड (1) < का उत्पादन करेगा? ऑब्जेक्ट> test1 < बढ़ाता है? ऑब्जेक्ट बढ़ाता है,? ऑब्जेक्ट>
बी बढ़ाता है। क्लॉज (2) वास्तव में वास्तविक प्रकार पैरामीटर
सी में परिभाषित किया गया है। खण्ड (3) < वस्तु> test3 < वस्तु, सूची < वस्तु >>

सवालों का उत्पादन करेगा:

  1. क्यों खंड (1) < वस्तु का उत्पादन नहीं किया>? चूंकि < ऑब्जेक्ट> क्लॉज (2) में दिखाए गए अनुसार काम करता है, < क्यों? ऑब्जेक्ट> इसके बजाय उत्पादन का विस्तार करता है?
  2. क्यों खंड (3) < ऑब्जेक्ट> < के बजाय ऑब्जेक्ट का उत्पादन? ऑब्जेक्ट बढ़ाता है>?
  3. चूंकि खंड (4) एक ही चर का उपयोग करता है, क्यों 2 अलग-अलग प्रकार के कैप्चर उत्पन्न होते हैं, इसलिए पैरामीटर का उपयोग समान चर डी का होता है?
+0

"यदि आप प्रत्येक कर्सर पर अपना कर्सर ले जाते हैं" - जो आईडीई कृपया? (अद्यतन: उस संपादन के लिए धन्यवाद) –

+0

ग्रहण, वह कहता है;) –

+0

क्लॉज 4 क्या है? –

उत्तर

4

क्यों खंड (1) < वस्तु का उत्पादन नहीं किया>? चूंकि < ऑब्जेक्ट> क्लॉज (2) में दिखाए गए अनुसार काम करता है, < क्यों? ऑब्जेक्ट> इसके बजाय उत्पादन का विस्तार करता है?

यह तीनों में से सबसे अच्छा सवाल है। मेरी सोच यह है कि संकलक/ग्रहण यह मानना ​​नहीं चाहता कि Object आवश्यक है T जो कि String और Integer के बीच अनुमानित है, इसलिए यह इसे सुरक्षित करता है। @bringer128 ने बताया, String और Integer दोनों Serializable और Comparable लागू करते हैं - इसलिए ये प्रकार अनुमानित प्रकार के विधि के लिए भी उम्मीदवार हैं।

यह ध्यान देने योग्य है कि निम्नलिखित कोड संकलक त्रुटि "प्रकार के अवैध शुरू" देता है लायक है:

GenericMethodInference.<? extends Object>test1("Hello", new Integer(1)); 

इसका कारण यह है कि यह एक विधि के प्रकार पैरामीटर के रूप में एक वाइल्डकार्ड निर्दिष्ट करने के लिए अवैध है। तो तथ्य यह है कि आप देख रहे हैं कि टूलटिप में इस जानकारी की रिपोर्ट करने के लिए कंपाइलर/ग्रहण की सुविधा के एक सूक्ष्मता के साथ करना है - यह निर्धारित करता है कि केवल T इसकी सीमाओं के भीतर है, न कि यह क्या है।

याद रखें कि जेनिक्स के जावा का कार्यान्वयन पूरी तरह से प्रोग्रामर की सुविधा/स्वच्छता के लिए है। एक बार बाइटकोड में संकलित हो जाने पर, type erasureT की किसी भी धारणा से छुटकारा पा लिया होगा। तो इसकी जांच में, संकलक को केवल यह सुनिश्चित करने की आवश्यकता है कि मान्य T अनुमानित किया जा सकता है, लेकिन यह आवश्यक नहीं है कि यह क्या है।


क्यों खंड (3) < वस्तु का उत्पादन> बजाय <? ऑब्जेक्ट बढ़ाता है>?

क्योंकि इस मामले में, तथ्य यह है कि एक List<Object> जहां एक List<T> की उम्मीद है में पारित हो जाता है संकलक कि T बिल्कुल Object है बताता है।


के बाद से खंड (4) एक ही चर का उपयोग करता है, यही कारण है कि 2 अलग प्रकार का कब्जा इस्तेमाल किया पैरामीटर eventhough उत्पन्न एक ही चर घ की है?

संकलक के लिए यह सुरक्षित नहीं है कि d वास्तव में पैरामीटर का मूल्यांकन करने के बीच भी उसी वस्तु को संदर्भित करता है। उदाहरण के लिए:

test4(d,(d = new ArrayList<String>())); 

इस मामले में, एक List<Integer> पहले पैरामीटर में पारित कर दिया जाएगा, और दूसरा में एक List<String> - दोनों d से। चूंकि यह परिदृश्य संभव है, इसलिए कंपाइलर इसे सुरक्षित खेलने के लिए आसान है।

+1

FYI क्लॉज 1 के लिए उनके पास सामान्य माता-पिता इंटरफेस सीरियलज़ेबल और तुलनात्मक हैं। कंपाइलर हमेशा उन विशिष्ट इंटरफ़ेस का अनुमान लगाने का प्रयास करेगा जो वे पा सकते हैं और ऑब्जेक्ट को हल करना पसंद नहीं करते हैं, अगर इससे मदद मिलती है। – Bringer128

+0

@ Bringer128 - बढ़िया बिंदु, मैं इसे अपने उत्तर में जोड़ दूंगा। –

+1

ओह, और मैं इसे 'स्थिर सूची test1 (टी टी 1, टी टी 2) {वापसी शून्य;}' और फिर एक चर (Ctrl + Alt + v) आधारित उत्पन्न करने के लिए आईडीई का उपयोग करके विधि को फिर से परिभाषित करके IntelliJ में परीक्षण करता हूं। विधि के वापसी मूल्य पर। मुझे लगता है कि नेटबीन कुछ ऐसा ही कर सकते हैं। – Bringer128

2

test1() केस वास्तव में काफी भयावह है। जेएलएस 3 15.12.2.7 देखें।

हमें टाइप अनुमान के विवरणों को नहीं जानना चाहिए - ज्यादातर मामलों में अंतर्ज्ञान एल्गोरिदम के साथ मेल खाता है। हां, यह हमेशा मामला नहीं है, जैसा प्रतीत होता है कि मामूली test1() उदाहरण में।

की कमी हमारे पास T :> String और T :> Integer

यह T=lub(String,Integer) की ओर जाता है (":>" सुपर प्रकार का मतलब है), lub का अर्थ है "कम से कम ऊपरी बाध्य है।"

String <: Comparable<String> और Integer <: Comparable<Integer> के बाद से, यह lci({Comparable<String>, Comparable<Integer>}) की ओर जाता है, जो Comparable<? extends lub(String,Integer)> पैदावार है, यानी Compable<? extends T>

अंत में, हम T = Serializable & Compable<? extends T> है, एक आत्म संदर्भित परिभाषा! स्पेक इसे "अनंत प्रकार" कहते हैं:

यह संभव है कि ऊपर की प्रक्रिया एक अनंत प्रकार उत्पन्न करे। यह अनुमत है, और जावा कंपाइलर्स को ऐसी परिस्थितियों को पहचानना चाहिए और चक्रीय डेटा संरचनाओं का उपयोग करके उचित रूप से उनका प्रतिनिधित्व करना चाहिए।

चलो पता कैसे javac यह प्रतिनिधित्व करते हैं: (javac 7)

static <T> T test1(T t1, T t2) {} 

public static void main(String[] args) 
{ 
    Void x = test1("Hello", new Integer(1)); 
} 

error: incompatible types 
required: Void 
found: INT#1 
where INT#1,INT#2 are intersection types: 
INT#1 extends Object,Serializable,Comparable<? extends INT#2> 
INT#2 extends Object,Serializable,Comparable<?> 

यही नहीं सही लगता है, यह वास्तव में रिकर्सिव नहीं है; ऐसा प्रतीत होता है कि जावैक lub() में रिकर्सन का पता लगाता है और छोड़ देता है, जिसके परिणामस्वरूप कम विशिष्ट प्रकार Comparable<?>

संबंधित मुद्दे