2016-06-29 24 views
41

मैं कोड का एक टुकड़ा है कि मुझे सोच क्यों इसे सफलतापूर्वक संकलित पर ठोकर खाई है:यह सामान्य कोड जावा 8 में क्यों संकलित करता है?

public class Main { 
    public static void main(String[] args) { 
     String s = newList(); // why does this line compile? 
     System.out.println(s); 
    } 

    private static <T extends List<Integer>> T newList() { 
     return (T) new ArrayList<Integer>(); 
    } 
} 

दिलचस्प बात है कि अगर मैं <T extends ArrayList<Integer>> साथ विधि newList के हस्ताक्षर को संशोधित इसे अब और काम नहीं करता है। टिप्पणी के बाद

अद्यतन & प्रतिक्रियाएं: अगर मैं वर्ग के लिए विधि से सामान्य प्रकार के लिए कदम कोड अब और संकलन नहीं करता है:

public class SomeClass<T extends List<Integer>> { 
    public void main(String[] args) { 
     String s = newList(); // this doesn't compile anymore 
     System.out.println(s); 
    } 

    private T newList() { 
     return (T) new ArrayList<Integer>(); 
    } 
} 
+3

संबंधित http://stackoverflow.com/questions/36402646/generic-return-type-upper-bound-interface-vs-class-surprisingly- valid-code – Tunaki

+1

अद्यतन करने का कारण यह है कि पहला यदि कोई बाधा संतोषजनक संतुष्ट हो तो संस्करण काम कर सकता है (टी सूची और टी स्ट्रिंग को बढ़ाता है) [यानी यह एक अस्तित्वत्मक मात्रा है], जबकि दूसरा संस्करण केवल तभी काम कर सकता है जब कक्षा में घोषणा से मेल खाने वाले सभी संभावित प्रकार टी स्ट्रिंग का विस्तार भी करते हैं [यानी। यह एक सार्वभौमिक मात्रा है]। –

उत्तर

32

आप एक विधि पर एक प्रकार पैरामीटर घोषित हैं, तो आप फोन करने वाले है, जब तक कि वास्तविक प्रकार की कमी को पूरा करेंगे इसके लिए एक वास्तविक प्रकार चुनने को अनुमति दे रहे हैं। उस प्रकार को एक वास्तविक ठोस प्रकार नहीं होना चाहिए, यह एक अमूर्त प्रकार, एक प्रकार चर या एक चौराहे प्रकार हो सकता है, अन्य में, अधिक बोलचाल शब्द, एक काल्पनिक प्रकार। तो, as said by Mureinik, String का विस्तार करने और List लागू करने वाला एक प्रकार हो सकता है। हम स्वयं मंगलाचरण के लिए एक चौराहे के प्रकार का उल्लेख नहीं कर सकते, लेकिन हम तर्क प्रदर्शित करने के लिए एक प्रकार चर का उपयोग कर सकते हैं:

public class Main { 
    public static <X extends String&List<Integer>> void main(String[] args) { 
     String s = Main.<X>newList(); 
     System.out.println(s); 
    } 

    private static <T extends List<Integer>> T newList() { 
     return (T) new ArrayList<Integer>(); 
    } 
} 
बेशक

, newList() इस तरह के एक प्रकार के लौटने की उम्मीद को पूरा नहीं कर सकते हैं, लेकिन यह है इस विधि की परिभाषा (या कार्यान्वयन) की समस्या। ArrayList से T पर कास्टिंग करते समय आपको "अनचेक" चेतावनी मिलनी चाहिए। एकमात्र संभव सही कार्यान्वयन यहां null लौटाएगा, जो विधि को बेकार प्रदान करता है।

प्रारंभिक कथन दोहराने के लिए बिंदु यह है कि एक सामान्य विधि के कॉलर प्रकार पैरामीटर के लिए वास्तविक प्रकार चुनता है। इसके विपरीत, जब आप एक सामान्य वर्ग तरह प्रकार पैरामीटर

public class SomeClass<T extends List<Integer>> { 
    public void main(String[] args) { 
     String s = newList(); // this doesn't compile anymore 
     System.out.println(s); 
    } 

    private T newList() { 
     return (T) new ArrayList<Integer>(); 
    } 
} 

साथ वर्ग के अनुबंध का हिस्सा है की घोषणा है, तो जो कोई भी बनाता है एक उदाहरण है कि उदाहरण के लिए वास्तविक प्रकार का चुनाव करेगा। उदाहरण विधि main उस वर्ग का हिस्सा है और उसे उस अनुबंध का पालन करना होगा।आप T नहीं चुन सकते हैं; T के लिए वास्तविक प्रकार सेट किया गया है और जावा में, आप आमतौर पर यह भी नहीं पता कि T क्या है।

जेनेरिक प्रोग्रामिंग का मुख्य बिंदु कोड लिखना है जो स्वतंत्र रूप से काम करता है कि किस प्रकार के प्रकार पैरामीटर के लिए चुना गया है।

लेकिन ध्यान दें कि आप अन्य बना सकते हैं, जो भी प्रकार आप पसंद करते हैं और विधि का आह्वान करते हैं, उदाहरण के साथ स्वतंत्र उदाहरण।

public class SomeClass<T extends List<Integer>> { 
    public <X extends String&List<Integer>> void main(String[] args) { 
     String s = new SomeClass<X>().newList(); 
     System.out.println(s); 
    } 

    private T newList() { 
     return (T) new ArrayList<Integer>(); 
    } 
} 

यहां, नए उदाहरण के निर्माता उस उदाहरण के लिए वास्तविक प्रकार चुनते हैं। जैसा कि कहा गया है, वास्तविक प्रकार को ठोस प्रकार की आवश्यकता नहीं है।

17

मेरा अनुमान है कि इस वजह से List एक है इंटरफेस। अगर हम इस तथ्य को अनदेखा करते हैं कि Stringfinal एक सेकंड के लिए है, तो आप सिद्धांत में, extends String (जिसका अर्थ है कि आप इसे s पर असाइन कर सकते हैं) लेकिन implements List<Integer> (जिसका अर्थ है कि इसे newList() से वापस किया जा सकता है)। एक बार जब आप एक इंटरफ़ेस (T extends List) से एक कंक्रीट क्लास (T extends ArrayList) पर वापसी प्रकार बदलते हैं तो संकलक यह समझ सकता है कि वे एक दूसरे से असाइन नहीं किए जा सकते हैं, और एक त्रुटि उत्पन्न करते हैं।

यह, String वास्तव में final से टूट गया है, और हम संकलक को इसे ध्यान में रख सकते हैं। आईएमएचओ, यह एक बग है, हालांकि मुझे यह स्वीकार करना होगा कि मैं कोई कंपाइलर-विशेषज्ञ नहीं हूं और इस बिंदु पर final संशोधक को अनदेखा करने का एक अच्छा कारण हो सकता है।

+1

हम्म, यह वास्तव में वास्तव में एक अच्छा बिंदु है। मैंने कक्षा बनाने की संभावना के बारे में नहीं सोचा था जो सिद्धांत में स्ट्रिंग और लागू सूची दोनों को बढ़ा सकता है। जैसा कि आपने कहा था कि यह वास्तव में लागू नहीं होता है क्योंकि स्ट्रिंग अंतिम है, लेकिन यह एक दिलचस्प विचार है। –

+7

यह तथ्य कि संकलन समय पर एक वर्ग 'अंतिम' है, ऐसी परिस्थितियों में कभी भी ध्यान नहीं दिया जाता है क्योंकि इस बात की कोई गारंटी नहीं है कि कक्षा अभी भी रनटाइम पर 'अंतिम' होगी। – Holger

+0

@errantlinguist मुझे यकीन नहीं है कि मैं जो कह रहा हूं उसका पालन करता हूं। समस्या यह है कि प्रस्तुत कोड संकलित करता है जब मैं टाइप प्रकार का मान निर्दिष्ट करता हूं, स्ट्रिंग के प्रकार के मान को सूची में विस्तारित करता है। –

6

मुझे नहीं पता कि यह संकलन क्यों है। दूसरी ओर, मैं समझा सकता हूं कि आप संकलन-समय जांच का पूरी तरह से लाभ कैसे उठा सकते हैं।

तो, newList() एक सामान्य विधि है, इसमें एक प्रकार का पैरामीटर है। आप इस पैरामीटर निर्दिष्ट करते हैं, तो संकलक की जाँच करेगा कि आप के लिए:

List<Integer> l = Main.<ArrayList<Integer>>newList(); // this compiles and works well 
System.out.println(l); 

thetype पैरामीटर निर्दिष्ट करना:

String s = Main.<String>newList(); // this doesn't compile anymore 
System.out.println(s); 

संकलन कदम पासेस:

संकलित करने के लिए विफल

टी वह पैरामीटर टाइप केवल संकलन-समय की जांच प्रदान करते हैं। यह डिज़ाइन द्वारा है, जावा सामान्य प्रकार के लिए type erasure का उपयोग करता है। आपके लिए कंपाइलर काम करने के लिए, आपको कोड में उन प्रकारों को निर्दिष्ट करना होगा। उदाहरण के निर्माण

के सर्वाधिक मामले में

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

List<String> list = new ArrayList<>(); 

यहाँ हम देख सकते हैं कि List<String> सूची आइटम के लिए प्रकार निर्दिष्ट करता है। दूसरी तरफ, नया ArrayList<>() नहीं है। यह इसके बजाय diamond operator का उपयोग करता है। अर्थात। जावा कंपाइलर infers घोषणा के आधार पर प्रकार।विधि मंगलाचरण

पर

अंतर्निहित प्रकार पैरामीटर जब आप एक स्थिर विधि आह्वान है, तो आप किसी अन्य तरीके से प्रकार निर्दिष्ट करने के लिए है।

public static <T extends Number> T max(T n1, T n2) { 
    if (n1.doubleValue() < n2.doubleValue()) { 
     return n2; 
    } 
    return n1; 
} 

, जिसे आप इस तरह इसका इस्तेमाल कर सकते हैं::

int max = max(3, 4); // implicit param type: Integer 

या इस तरह:

double max2 = max(3.0, 4.0); // implicit param type: Double 

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

उदाहरण के लिए कहें, इस तरह आप एक प्रकार सुरक्षित खाली सूची बना सकते हैं:

List<Integer> noIntegers = Collections.<Integer>emptyList(); 

प्रकार पैरामीटर <Integer> विधि emptyList() को पारित कर दिया है। एकमात्र बाधा यह है कि आपको कक्षा भी निर्दिष्ट करनी है। अर्थात। आप ऐसा कर सकते हैं नहीं:

import static java.util.Collections.emptyList; 
... 
List<Integer> noIntegers = <Integer>emptyList(); // this won't compile 

रनटाइम प्रकार टोकन

इन चालों में से कोई भी आपकी मदद कर सकता है, तो आप एक runtime type token निर्दिष्ट कर सकते हैं। अर्थात। आप एक वर्ग के रूप में एक वर्ग प्रदान करते हैं। एक सामान्य उदाहरण EnumMap है:

private static enum Letters {A, B, C}; // dummy enum 
... 
public static void main(String[] args) { 
    Map<Letters, Integer> map = new EnumMap<>(Letters.class); 
} 
+0

क्या आप स्पष्टीकरण को सरल बना सकते हैं? –

+0

सरल उत्तर यह है कि आप '<> 'ब्रैकेट के भीतर प्रकार पैरामीटर निर्दिष्ट कर सकते हैं।अन्यथा मैंने स्पष्टीकरण दिया है ताकि आप सभी विकल्पों को देख सकें। –

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