2013-03-07 7 views
13

के मेरे पास है लगता है निम्नलिखित हैं:मुझे सामान्य कॉल को स्पष्ट रूप से क्यों डालने की आवश्यकता है?

public <T extends Widget> List<T> first(T n) { 
    return first(n.getClass()); 
} 
public <T extends Widget> List<T> first(Class<T> n) { 
    return new ArrayList<>(); 
} 

संकलक "incompatible types; required: java.util.List<T>; found: java.util.List<capture#1 of ? extends my.app.Widget>" के साथ लाइन 3 पर शिकायत। जो मुझे समझ में नहीं आता क्यों। यह मेरे लिए उचित लगता है कि प्रकार T उप-प्रकार के अलावा किसी अन्य मामले में कभी भी नहीं बदला जा सकता है।

इसे स्पष्ट कास्टिंग के माध्यम से तय किया जा सकता है, हालांकि मुझे नहीं पता कि इसकी आवश्यकता क्यों है।

public <T extends Widget> List<T> first(T n) { 
    return (List<T>)first(n.getClass()); 
} 
public <T extends Widget> List<T> first(Class<T> n) { 
    return new ArrayList<>(); 
} 

क्या यह एक कंपाइलर बग हो सकता है?

नोट मैं JDK 1.7.0_15 उपयोग कर रहा हूँ:

java version "1.7.0_15" 
Java(TM) SE Runtime Environment (build 1.7.0_15-b03) 
Java HotSpot(TM) 64-Bit Server VM (build 23.7-b01, mixed mode) 

उत्तर

7

ठीक कहने के कारण, टाइप पैरामीटर वास्तव में आपके द्वारा पारित ऑब्जेक्ट के वास्तविक रनटाइम प्रकार का सुपरटेप हो सकता है!T#getClass() वापस नहीं करता है Class<T>, यह रिटर्न Class<? extends T>

Number number = Integer.valueOf(1); 
List<Number> list = first(number); 

जब आप रनटाइम पर n.getClass() फोन Integer.class, नहीं Number.class वापस जाने के लिए जा रहा है, फिर भी आप List<Number> लिए परिणाम असाइन करने की कोशिश कर रहे हैं! कंपाइलर को यह जानने का कोई तरीका नहीं है कि असली रनटाइम प्रकार क्या होगा, यह केवल इतना जानता है कि List<? extends Number> वापस आ गया है। आपको कलाकारों को डालने के लिए मजबूर करना यह कहने का तरीका है कि "मैं इस ऑपरेशन की सुरक्षा के लिए झुकाव नहीं कर सकता, यह आपके शब्द पर है कि यह सही है।"

किसी भी समय यह संकलन करने के लिए असंभव है कि एक ऑपरेशन टाइपएफ़ है, यह आपको कास्ट करने के लिए मजबूर करेगा और इस तरह एक "अनचेक" चेतावनी लेगा ताकि उसने आपको समस्या के बारे में बताने का काम किया हो।

+0

मैंने आपके उत्तर को कुछ बार पढ़ा और सोचा कि मुझे समझ में आया, लेकिन अब मुझे पूरा यकीन नहीं है। मान लीजिए 'सर्कल' और 'स्क्वायर' 'विजेट' के उप-वर्ग हैं। अगर मैं 'प्रथम (Circle.class)' कहता हूं, तो यह केवल 'सूची <के लिए संभव है? सर्किल> 'वापस आने के लिए, यानी मैं नई ArrayList ' वापस नहीं कर सकता क्योंकि संकलक मुझे अनुमति नहीं देगा। –

+4

'Circle.class' एक संकलन समय निरंतर है, यह * नया सर्किल()। GetClass()' जैसा नहीं है जिसे रनटाइम तक हल नहीं किया गया है। यदि आपने 'पहला (Circle.class)' कहा है, तो यह 'ठीक ' वापस ठीक कर सकता है, लेकिन यदि आप वास्तविक प्रकार को खोजने के लिए रन टाइम तक प्रतीक्षा नहीं करते हैं। – Affe

+0

मेरे भ्रम में जोड़कर मुझे आश्चर्य हुआ कि मैं 'सूची y = पहले (n.getClass()) भी नहीं कर सकता; हालांकि, निश्चित रूप से वापस लौटाई गई सूची केवल वही हो सकती है, जैसा आपने कहा था? विजेट को बढ़ाता है, जहां तक ​​मैं समझता हूं कि इस परिणामी सूची में केवल 'विजेट' या वंशज शामिल हो सकते हैं? –

4

getClass() प्रकार Class<?> का एक मान देता है। जब आप इसे first के अन्य ओवरराइड पर पास करते हैं, तो आप Class<? extends Widget> पर एक अनचेक कास्ट कर रहे हैं। यह अनचेक है क्योंकि जेनेरिक रनटाइम पर "टाइप एरर" का उपयोग करते हैं, जिसका अर्थ है कि जेवीएम तब जांच नहीं सकता है; इस चेतावनी को देखने के लिए -Xlint:unchecked पास करें।

ध्यान दें कि यह Class<T extends Widget> नहीं है, जिसका अर्थ है कि प्रकार व्यवस्था सुनिश्चित नहीं है कि इस विधि (first के पहले ओवरराइड) के प्रकार के पैरामीटर एक की तरह ही बुलाया जा रहा है (अन्य first) है।

तो परिणाम वापस पहुंचने के प्रकार List<? extends Widget> (capture#1 जहां ? extends Widget है) है, जो List<T extends Widget> साथ संगत नहीं है, और इसलिए संकलक सही ढंग से एक त्रुटि पैदा करता है की है।

इस मामले में, आपको पता चल जाता है (हालांकि संकलक नहीं करता है) कि यह एक उचित बात है, ताकि आप इसे एक स्पष्ट कलाकार के साथ ओवरराइड कर सकें। लेकिन उस मामले में, क्यों सिर्फ विधि इस बनाने के लिए नहीं:

public <T extends Widget> List<T> first() { 
    return new ArrayList<T>(); 
} 
+0

भी – bsiamionau

+0

क्या नहीं होगा संकलन नहीं होगा? मेरा आखिरी उदाहरण? मैं बस वापसी प्रकार भूल गया (हालांकि यह संभवतः स्पष्ट था)। –

+0

इसके अलावा, आप सही थे, मेरी गलती। – bsiamionau

1

जेनेरिक्स और उनके संबंधित अंतिम प्रकार, संकलन समय पर निर्धारित कर रहे हैं क्रम में नहीं। रनटाइम पर, सभी सामान्य जानकारी मिटा दी जाती है - इसे Type erasure के रूप में जाना जाता है। ऐसा लगता है कि यहाँ आप समारोह कॉल है, जो जावा

public <T extends Widget> List<T> first(T n) { 
    return first(n.getClass()); 
} 

यहाँ में समर्थित नहीं है के समय में सामान्य प्रकार का निर्धारण करने के लिए कोशिश कर रहे हैं, तो आप List<T> वापस जाने के लिए की जरूरत है - सूची का एक उदाहरण। एक ठोस उदाहरण में एक ठोस पैरामीटर T होना चाहिए। अर्थात। कोई List<T extends Widget> वापस नहीं कर सकता - प्रकार अज्ञात होगा। आप इस तरह की सूची में क्या स्टोर करेंगे?

public <T extends Widget> List<T> first(Class<T> n) { 
    return new ArrayList<>(); 
} 

यहाँ आप की आपूर्ति की कक्षा के आधार पर एक सूची वापस जाने के लिए चाहते हैं - लेकिन देखते हैं कि आप वास्तव में एक सामान्य ArrayList जो तब आवश्यक प्रकार के casted है बना रहे हैं। समस्या यह है कि टी यहां पिछली विधि में टी के समान नहीं है। आपको एक ही टी बनाने के लिए पूरे वर्ग जेनेरिक को एक टी के साथ घोषित करने की आवश्यकता होगी।

3

ऐसा इसलिए है क्योंकि n.getClass()Class<T> के बजाय हमेशा Class<?> लौटाता है इसलिए संकलक सीधे इसे हल नहीं कर सकता है। आपको हमेशा स्पष्ट रूप से डालने के लिए यहां आवश्यकता है। उदाहरण

लिए
public <T extends Widget> List<T> first(T n) { 
    return first((Class<T>)n.getClass()); 
} 
संबंधित मुद्दे

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