2011-03-25 15 views
7

जावा में प्रत्येक लूप के लिए जेनेरिक का उपयोग करते समय मुझे एक अजीब कंपाइलर त्रुटि मिली। क्या यह जावा कंपाइलर बग है, या क्या मैं वास्तव में यहां कुछ खो रहा हूं?जावा कंपाइलर कच्चे प्रकार के साथ foreach का उपयोग करने पर शिकायत क्यों करता है?

public class Generics<T extends Object> { 
    public Generics(T myObject){ 
    // I didn't really need myObject 
    } 

    public List<String> getList(){ 
    List<String> list = new ArrayList<String>(); 
    list.add("w00t StackOverflow"); 
    return list; 
    } 

    public static void main(String...a){ 
    Generics generics = new Generics(new Object()); 
    for(String s : generics.getList()){ 
     System.out.println(s); 
    } 
    } 
} 

संकलक के साथ के लिए-प्रत्येक पंक्ति के बारे में शिकायत कर रहा है:

यहाँ मेरी पूरी कक्षा है। "प्रकार बेमेल तत्व प्रकार वस्तु से परिवर्तित नहीं कर सकते स्ट्रिंग के लिए"
मैं इस सूक्ष्म बदलाव करते हैं, यह संकलित:

public static void main(String...a){ 
    Generics<?> generics = new Generics(new Object()); 
    for(String s : generics.getList()){ 
    System.out.println(s); 
    } 
} 

मैं जानता हूँ कि getList() उपयोग जेनरिक करता है, लेकिन यह मैं क्या सोचा था पूरी तरह से असंबद्ध तरीका था में उन्हें उपयोग करता है। मैं इसे समझ सकता था अगर मैं किसी प्रकार के टी और getList() पर पुन: प्रयास करने की कोशिश कर रहा था, तो List<T> या कुछ लौटा, लेकिन यह मामला यहां नहीं है। getList() का रिटर्न प्रकार टी के साथ बिल्कुल कुछ नहीं होना चाहिए और मुझे परवाह नहीं करना चाहिए कि मैं अपने जेनेरिक ऑब्जेक्ट के लिए कच्चे प्रकार का उपयोग करता हूं या नहीं ... सही? क्या ये पूरी तरह से असंबंधित नहीं होना चाहिए, या क्या मैं वास्तव में यहां कुछ खो रहा हूं?

ध्यान दें कि कोड भी संकलित अगर मैं इस, जो मैंने सोचा था कि करने के लिए पहले के रूप में अच्छी बराबर होना चाहिए कार्य करें:

public static void main(String...a){ 
    Generics generics = new Generics(new Object()); 
    List<String> list = generics.getList(); 
    for(String s : list){ 
    System.out.println(s); 
    } 
} 
+1

क्या लगेगा है '<टी वस्तु फैली>' ' से अलग नहीं है'। आप अपने वर्ग का एक सामान्य संस्करण नहीं बना रहे हैं, आप कच्चे प्रकार का बना रहे हैं। जो हमें इस सवाल पर लाता है कि आपकी कक्षा सामान्य जगह क्यों है? टी का उपयोग करने वाली एकमात्र जगह निर्माता में है और आप उस संदर्भ का उपयोग नहीं करते हैं। – unholysampler

+0

मैंने '<टी ऑब्जेक्ट बढ़ाया>' का उपयोग किया क्योंकि मुझे बस एक उदाहरण के लिए कुछ चाहिए था। असली कोड स्पष्ट रूप से कुछ और है, और यह टी का उपयोग करता है ... यह सिर्फ 'getList() 'से पूरी तरह से असंबंधित तरीके से टी का उपयोग करता है। –

+0

आपके प्रश्न से असंबद्ध है, लेकिन मैं निर्माता को जेनेरिक सीएलएस बना दूंगा) ताकि आपको इस जेनिक्स क्लास को बनाने के लिए टाइप टी के किसी ऑब्जेक्ट को तुरंत चालू करने की आवश्यकता न हो। – MeBigFatGuy

उत्तर

11

अंतर यह है कि जब आप कच्चे प्रकार का उपयोग करते हैं, सभी सदस्य हस्ताक्षरों के सामान्य संदर्भ भी उनके कच्चे रूप में परिवर्तित हो जाते हैं। तो प्रभावी रूप से आप एक तरीका है जिसके अब इस तरह की एक हस्ताक्षर हैं कॉल कर रहे हैं: क्यों अपने अंतिम संस्करण को संकलित करता है के लिए के रूप में अब

List getList() 

- हालांकि यह होता है, वहाँ एक चेतावनी है कि अगर आप का उपयोग -Xlint:

Generics.java:16: warning: [unchecked] unchecked conversion 
    List<String> list = generics.getList(); 
             ^

इस के समान है:

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

... जो भी संकलित है, लेकिन -Xlint के तहत एक चेतावनी के साथ।

कहानी का नैतिक: कच्चे प्रकार का उपयोग न करें!

+0

मुझे आश्चर्य है कि * सभी * सदस्य हस्ताक्षर के भीतर सामान्य संदर्भ उनके कच्चे रूप में परिवर्तित हो जाते हैं। ऐसा करने का तर्क क्या है (उस सूर्य के अलावा बस ऐसा महसूस किया)? –

+4

@ माइकल: जेएलएस में धारा 4.8 (कच्चे प्रकार) में इस चर्चा को शामिल किया गया है: "कच्चे प्रकार वाइल्डकार्ड से निकटता से संबंधित हैं। दोनों अस्तित्वहीन प्रकारों पर आधारित हैं। कच्चे प्रकारों को वाइल्डकार्ड के रूप में सोचा जा सकता है जिनके प्रकार के नियम जानबूझकर बेकार हैं, समायोजित करने के लिए विरासत कोड के साथ बातचीत। " दूसरे शब्दों में, कच्चे प्रकार को आम तौर पर नए कोड में नहीं दिखाना चाहिए, लेकिन उन्होंने पुराने कोड को संकलित करने में विफल होने से बचने की कोशिश की, भले ही यह कम से कम संदिग्ध हो। –

+0

बहुत दिलचस्प है। मैं पहले से ही कच्चे प्रकारों का उपयोग करने से बचने के लिए जानता था (एक सहकर्मी ने चर घोषित करने के लिए कोड लिखा था), लेकिन यह दर्शाता है कि यह वास्तव में मायने रखता है। –

3

बदलें लाइन

Generics generics = new Generics(new Object()); 

Generics<?> generics = new Generics<Object>(new Object()); 
को

आपकी समस्या का मूल यह है कि आप raw type का उपयोग कर रहे हैं, इसलिएका प्रकारविधि List है, List<String> नहीं।

+0

जेनिक्स स्ट्रिंग प्रकार के जेनेरिक नहीं होने वाला है ... यह पूरा बिंदु है। स्ट्रिंग जेनेरिक के प्रकार से असंबंधित है। टी के बावजूद 'getList()' को 'सूची ' वापस करनी चाहिए। –

+0

@ माइकल मैकगोवन, अच्छा बिंदु। लेकिन घोषणा के बिंदु पर प्रकार पैरामीटर से जुड़े कुछ प्रकार होना चाहिए। 'जेनेरिक जेनिक्स = नई जेनरिक (...);' ठीक होगा, मॉड्यूल एक असुरक्षित रूपांतरण चेतावनी। –

+0

@ माइकल मैकगोवन, ध्यान दें कि यदि आपने जो कुछ किया है, वह वर्ग घोषणा से टाइप पैरामीटर '<टी ऑब्जेक्ट>> बढ़ाता है, तो यह काम करेगा। –

-1

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

यहाँ नए मुख्य तरह

public static void main(String...a){ 
    Generics<String> generics = new Generics<String>(); 
    for(String s : generics.getList()){ 
     System.out.println(s); 
    } 
    } 
+0

जेनरिक स्ट्रिंग प्रकार के जेनेरिक नहीं होने वाला है ... यह पूरा बिंदु है। स्ट्रिंग जेनेरिक के प्रकार से असंबंधित है। –

+0

मुझे लगता है कि आपने मेरे बिंदु को गलत समझा। यदि आप कोड को देखते हैं, तो आपको सूची पर लौटने वाली सूची() विधि मिलती है। अगर हम कोड को वास्तव में साफ करना चाहते थे तो हम जेनिक्स भाग को दूसरे कोड के बाद getList() विधि में छीन सकते थे। आप इसे संकलित करने में मदद के लिए पूछ रहे थे, फिर उसका दृष्टिकोण सही/गलत था। – Sean

+0

यदि आप कक्षा स्तर पर सामान्य मंदी को छोड़ देते हैं, तो आप अब मौजूद हार्ड कोडिंग को हटाने के लिए getList() विधि खोलें। – Sean