2010-07-23 4 views
5

क्या कोई मुझे बता सकता है कि ForEachLoop उदाहरण के लिए जेनेरिक प्रकार को असाइन करने की स्पष्ट आवश्यकता क्यों है?प्रत्येक लूप समस्या के लिए जेनेरिक यदि उदाहरण में सामान्य प्रकार नहीं है

क्यों संकलक शिकायत: प्रकार बेमेल: तत्व प्रकार वस्तु से स्ट्रिंग को परिवर्तित नहीं कर सकते?

JDK 1.5.0_09

import java.util.ArrayList; 
import java.util.Collection; 

public class ForEachLoop<T> { 

public static void main(String[] args) { 

    // Non functional version 
    ForEachLoop f = new ForEachLoop(); 

    // Functional version 
    //ForEachLoop<Integer> f = new ForEachLoop(); 

      // Type mismatch: cannot convert from element type Object to String 
    for(String a : f.getStrings()) { 
     System.out.println(a); 
    } 
} 

public Collection<String> getStrings() { 
    Collection<String> strings = new ArrayList<String>(); 
    strings.add("Hello"); 
    return strings; 
} 

} 

उत्तर

8

यह एक नहीं बल्कि आम गलती है:

ForEachLoop f = new ForEachLoop(); 

होना चाहिए

ForEachLoop<Something> f = new ForEachLoop<Something>(); 

आप (कच्चे प्रकार का उपयोग करते है जो आप नहीं करना चाहिए) संकलक उस उदाहरण के लिए सभी सामान्य जानकारी मिटा देगा, भले ही यह टाइप पैरामीटर टी नहीं है, इसे संगत बनाने के लिए पूर्व 1.5 कोड।

यदि आप जावा 1.4 या उससे कम के लिए लिख रहे हैं तो केवल कच्चे प्रकार का उपयोग करें, इस मामले में आपको कोई जेनरिक्स नहीं होना चाहिए। बाइटकोड स्तर पर विधि टाइप एरर के बाद संग्रह (कच्ची) लौटाती है। आम तौर पर, यदि उदाहरण में सामान्य प्रकार सेट होता है, तो जब आप संग्रह पर get करने का प्रयास करते हैं, तो संकलक जेनेरिक जानकारी का उपयोग यह तय करने के लिए करेगा कि उसे स्ट्रिंग वापस करनी चाहिए, और फिर बाइटकोड स्तर पर यह स्वचालित रूप से प्राप्त ऑब्जेक्ट को प्राप्त करता है संग्रह से स्ट्रिंग तक (क्योंकि यह स्ट्रिंग होने की गारंटी है)। लेकिन यदि आप कच्चे प्रकार का उपयोग करते हैं तो संकलक सभी सामान्य जानकारी को अनदेखा कर देगा और अब आपके लिए ऑब्जेक्ट को स्वचालित रूप से नहीं डालेगा।

संपादित: कच्चे प्रकार पर अनुभाग में इन बातों को कर रहे हैं:

उपरोक्त नियमों का एक अन्य निहितार्थ यह है कि एक कच्चे प्रकार की एक सामान्य भीतरी वर्ग में ही केवल एक के रूप में इस्तेमाल किया जा सकता है कच्चे प्रकार:

class Outer<T>{ 
    class Inner<S> { 
    S s; 
    } 
} 

यह आंशिक रूप से कच्चे प्रकार के रूप में इनर तक पहुँचने के लिए संभव नहीं है (एक "दुर्लभ" प्रकार)

Outer.Inner<Double> x = null; // illegal 
Double d = x.s; 

क्योंकि बाहरी ही कच्चे है, इसलिए इनर सहित सब अपने भीतर की कक्षाएं, कर रहे हैं, और इसलिए यह संभव यह करने के लिए किसी भी प्रकार के पैरामीटर पास नहीं है।

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

यह एक संकलन समय त्रुटि एक कच्चे प्रकार के रूप में एक पैरामिट्रीकृत प्रकार का एक प्रकार सदस्य का उपयोग करने के प्रयास करने के लिए है।

इसका मतलब है कि पर "दुर्लभ" प्रकार प्रतिबंध मामले में जहां योग्यता प्रकार पैरामिट्रीकृत है तक फैली हुई है, लेकिन हम एक कच्चे प्रकार के रूप में आंतरिक वर्ग उपयोग करने का प्रयास:

Outer<Integer>.Inner x = null; // illegal 

यह मामले के विपरीत हम ऊपर चर्चा की है। इस आधा बेक्ड प्रकार के लिए कोई व्यावहारिक औचित्य नहीं है। विरासत कोड में, पैरामीटर का कोई भी प्रकार उपयोग नहीं किया जाता है। गैर-विरासत कोड में, हमें सामान्य प्रकार सही ढंग से उपयोग करना चाहिए और सभी आवश्यक वास्तविक प्रकार पैरामीटर को पारित करना चाहिए।

ध्यान दें कि इनर क्लास में इसका स्वयं का प्रकार पैरामीटर बाहरी वर्ग में से एक से स्वतंत्र है, और यह अभी भी मिटा दिया जाता है। असल में वे नहीं चाहते हैं कि हम एक ही उदाहरण पर कच्चे और जेनेरिक प्रकारों को मिलाएं, क्योंकि यह किसी भी संस्करण में समझ में नहीं आता है (प्री 1.5 में, जेनेरिक हिस्सा एक त्रुटि होगी, 1.5+ में कच्चे प्रकार को हतोत्साहित किया जाता है, और यहां तक ​​कि भविष्य के संस्करणों से हटाया जा सकता है)

तो फिर वहाँ भी इस है:

एक निर्माता के प्रकार (§8.8), उदाहरण विधि (§8.8, §9.4), या गैर स्थिर क्षेत्र (§8.3) कच्चे प्रकार सी जो कि सुपरक्लास या superinterfaces से विरासत में नहीं मिला है जेनेरिक घोषणा में इसके प्रकार का क्षरण है सी के लिए इसी एक कच्चे प्रकार सी के एक स्थिर सदस्य की प्रकार सामान्य घोषणा में अपनी तरह करने के लिए इसी सी

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

जो कहता है कि रचनाकार, उदाहरण विधियों और गैर स्थैतिक क्षेत्रों को कच्चे उदाहरण में कच्चे के रूप में माना जाएगा। स्थिर सदस्यों को वैसे भी जेनेरिक माना जाएगा, क्योंकि उन्हें एक उदाहरण की आवश्यकता नहीं है।

+0

दिलचस्प लगता है और तार्किक। क्या आप कृपया जावा विनिर्देश को इंगित कर सकते हैं कि "कंपाइलर सभी सामान्य जानकारी मिटा देगा भले ही यह टाइप पैरामीटर टी नहीं है"? –

+0

जेएलएस से कुछ स्निपेट, और मेरी (शायद सीमित) समझ में जोड़ा गया। –

+0

मैं आपके उत्तर को अवशोषित करने की कोशिश करूंगा, लेकिन मुझे कुछ समय लगेगा। धन्यवाद –

0

क्या आप अक्रिय संस्करण में क्या होगा उम्मीद करते हैं? आपने टेम्पलेट क्लास ForEachLoop<T> घोषित कर दिया है, जो पूरी तरह योग्य होने तक अपूर्ण प्रकार है। यह परिवर्तन नहीं करता है केवल क्योंकि

  • अपने कोड टेम्पलेट वर्ग
  • आप वास्तव में टेम्पलेट पैरामीटर का उपयोग नहीं करते के अंदर है।
+0

मैं कोड भले ही मैं getStrings के रूप में टी पैरामीटर निर्दिष्ट नहीं करते compilable होने की अपेक्षा करेंगे() पर टी –

+0

ठीक है, मैं एक संकलक अनुमान लगाने में मैं क्या मतलब है की कोशिश नहीं कर रहा है की तरह निर्भर नहीं है। ज्यादातर मामलों में, यदि कोई टेम्पलेट पैरामीटर उपयोग नहीं किया जाता है, तो ऐसा इसलिए होगा क्योंकि मुझे कुछ याद आएगा। (आप अधिकतम सहनशीलता चाहते हैं, बल्कि जावा के अलावा ओल्ड-स्कूल पर्ल कोशिश करो!) –

0

जावा जेनेरिक्स Type Erasure का उपयोग करके लागू, जिसका अर्थ है कि संकलक एक सामान्य प्रकार (उदा ForEachLoop<Something>) है कि मूल रूप से "डाली आपत्ति करने के लिए" का उपयोग करता मुहावरा से प्रत्येक इन्स्टेन्शियशन के लिए कोड का उत्सर्जन होगा। तो आपकी लाइन ForEachLoop f = new ForEachLoop(); एक "सीधी" तात्कालिकता उत्पन्न करेगी जहां टाइप तर्क object पर डिफ़ॉल्ट होगा।

तो क्या तुम सच में एक ForEachLoop<object> और इसी कारण आप कास्टिंग त्रुटि मिलती है।

+0

वह सामान्य प्रकार का उपयोग नहीं करता वास्तव में, ForEachLoop भी काम करना चाहिए। –

+0

और टी को कहां रखा जाएगा? – musiKk

+0

@Musikk: वास्तव में टी करने के लिए डाली हर अवसर पर रहता है (वापसी प्रकार टी की एक विधि में उदा) "टी टी के रूप में प्रयोग किया जाता है"। –

1

पाश के लिए आप के बराबर अभिव्यक्ति (जावा भाषा कल्पना के अनुसार) है:

for (Iterator<String> i = f.getStrings().iterator(); i.hasNext();) { 
    String a = i.next(); 
    System.out.println(a); 
} 

ForEachLoop एक सामान्य वर्ग है, लेकिन f आप कच्चे प्रकार का इस्तेमाल किया। तो संकलक कि इटरेटर Object उदाहरणों या उप-प्रकारों के उदाहरण लौट सकते हैं ग्रहण करने के लिए है। और एक चेतावनी देता है, क्योंकि आप उस कच्चे इटरेटर को एक पैरामीकृत इटरेटर को असाइन करते हैं।

के रूप में अपने संग्रह के रूप में लंबे अंदर वह उदाहरण सिर्फ स्ट्रिंग्स शामिल ForEachLoop, आप एक रनटाइम त्रुटि नहीं देख सकेंगे।

चेतावनी से छुटकारा पाने के लिए, इन्स्टेन्शियशन हिस्सा parametize, जैसे:

ForEachLoop<String> f = new ForEachLoop<String>(); 
+0

किसी भी parametrization काम करना चाहिए, यहां तक ​​कि

+0

@Georgy साथ - सुनिश्चित करें, लेकिन '' का इस्तेमाल किया उसके प्रश्न में कोड इसलिए मैं अपने जवाब में एक ही प्रकार का फैसला किया। –

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