2016-02-10 8 views
14

जबकि Java8 स्ट्रीम पर अध्ययन कर के साथ प्रयोग किया समस्याएं, मैं निम्नलिखित कोड का टुकड़ा भर में आया था:कम सीमा को समझने जब लैम्ब्डा और कार्यात्मक इंटरफ़ेस

Predicate<? super String> predicate = s -> s.startsWith("g"); 

के बाद से सामान्य पैरामीटर एक निचली सीमा है, मैं समझ यह नहीं होगा संकलन। जिस तरह से मैं इसे देखता हूं, यदि कोई ऑब्जेक्ट स्ट्रिंग के लिए एक सुपरटेप है, तो किसी ऑब्जेक्ट प्रकार में गुजरना इसे तोड़ना चाहिए, क्योंकि ऑब्जेक्ट में प्रारंभ नहीं होता है() फ़ंक्शन। हालांकि, मुझे आश्चर्य हुआ कि यह बिना किसी समस्या के काम करता है।

इसके अलावा अभी तक, जब मैं एक ऊपरी बाध्य लेने के लिए विधेय बदलाव:

<? extends String>, 

यह संकलन नहीं होगा।

मैंने सोचा कि मुझे ऊपरी और निचली सीमाओं का अर्थ समझा गया है, लेकिन जाहिर है, मुझे कुछ याद आ रहा है। क्या कोई इस बात को समझाने में मदद कर सकता है कि इस भेड़ के बच्चे के साथ निचला बाध्य क्यों काम करता है?

+2

'संकलित करता है' भी - 'भविष्यवाणी करें predicate = s -> s.starts के साथ ("g") '- हालांकि, हम इस predicate के लिए' स्ट्रिंग' फ़ीड नहीं कर सकते हैं :) यह भी देखें [लेख] (http://bayou.io/draft/ कैप्चरिंग_Wildcards.html) वाइल्डकार्ड पर – ZhongYu

+1

http://stackoverflow.com/questions/33085151/bad-return-type-in-lambda-expression/33085893#33085893 – ZhongYu

+1

'पूर्वानुमान 'करता है * नहीं * यह दर्शाता है कि आप इसे 'ऑब्जेक्ट' पास कर सकते हैं (और ऐसा करने का प्रयास एक कंपाइलर त्रुटि उत्पन्न करेगा)। हस्ताक्षर 'भविष्यवाणी 'बस इसका तात्पर्य है कि यह * ' अनुमानित हो सकता है, ठीक है, क्योंकि यह अनुमान अभी भी 'स्ट्रिंग' इनपुट को संसाधित करने में सक्षम है। दूसरे शब्दों में, 'भविष्यवाणी करें 'कहता है कि यह अनुमान स्ट्रिंग का उपभोग कर सकता है, इसके वास्तविक प्रकार के बावजूद, तो' 'भविष्यवाणी करें' भविष्यवाणी <का वैध कार्यान्वयन है? सुपर स्ट्रिंग> '। – Holger

उत्तर

8

लैम्ब्डा तर्क प्रकार सटीक है, यह ? super या ? extends नहीं हो सकता है। यह JLS 15.27.3. Type of a Lambda Expression द्वारा कवर किया गया है। यह ग्राउंड लक्ष्य प्रकार अवधारणा (जो मूल रूप से लैम्ब्डा प्रकार है) पेश करता है। अन्य बातों के अलावा यह कहा जाता है कि:

तो T एक वाइल्डकार्ड-पैरामिट्रीकृत कार्यात्मक इंटरफ़ेस प्रकार है और लैम्ब्डा अभिव्यक्ति परोक्ष टाइप किया है, तो जमीन लक्ष्य प्रकार टी के गैर वाइल्डकार्ड parameterization (§9.9) है

जोर मेरा। तो अनिवार्य रूप से जब आप

Predicate<? super String> predicate = s -> s.startsWith("g"); 

आपका लैम्ब्डा प्रकार Predicate<String> है।

Predicate<? super String> predicate = (Predicate<String>)(s -> s.startsWith("g")); 

या यहां तक ​​कि

Predicate<String> pred = (Predicate<String>)(s -> s.startsWith("g")); 
Predicate<? super String> predicate = pred; 

तथ्य यह है कि lambdas प्रकार तर्क, ठोस हैं के बाद कि सामान्य रूपांतरण नियम लागू होते हैं को देखते हुए: Predicate<String> एक Predicate<? super String>, या Predicate<? extends String> है यह रूप में एक ही है। तो Predicate<? super String> और Predicate<? extends String> दोनों संकलित करना चाहिए। और दोनों वास्तव में javac 8u25, 8u45, 8u71 के साथ ही ecj 3.11.1 पर मेरे लिए काम करते हैं।

+0

स्पष्टीकरण के लिए धन्यवाद। बाध्य पैरामीटर के साथ लैम्ब्डा डिफ़ॉल्ट व्यवहार मेरे भ्रम के दिल में था। – piper1970

7

मैंने अभी इसका परीक्षण किया है, असाइनमेंट स्वयं संकलित करता है। क्या परिवर्तन यह है कि आप वास्तव में predicate.test() पर कॉल कर सकते हैं।

चलो एक कदम पीछे ले जाएं और स्पष्टीकरण के लिए प्लेसहोल्डर GenericClass<T> का उपयोग करें। तर्क तर्क के लिए, FooBar और Bar विस्तारित करता है Baz

का विस्तार: जब आप एक GenericClass<? extends Bar> घोषित, आप कह रहे हैं "मैं क्या इसके जेनेरिक प्रकार तर्क वास्तव में नहीं पता है, लेकिन यह Bar का एक उपवर्ग है।" वास्तविक उदाहरण में हमेशा एक गैर-वाइल्डकार्ड प्रकार तर्क होगा, लेकिन कोड के इस हिस्से में आप नहीं जानते कि इसका मूल्य क्या है। अब विचार करें कि विधि आमंत्रण के लिए इसका क्या अर्थ है।

आप जानते हैं कि आपको वास्तव में क्या मिला है या तो GenericClass<Foo> या GenericClass<Bar> है। T लौटने वाली विधि पर विचार करें। पूर्व मामले में, इसका रिटर्न प्रकार Foo है। बाद में, Bar। किसी भी तरह से, यह Bar का उप प्रकार है और Bar चर को असाइन करने के लिए सुरक्षित है।

एक ऐसी विधि पर विचार करें जिसमें T पैरामीटर है। यदि यह GenericClass<Foo> है, तो इसे Bar पास करना एक त्रुटि है - BarFoo का उप प्रकार नहीं है।

तो, ऊपरी बाउंड के साथ आप जेनेरिक रिटर्न मानों का उपयोग कर सकते हैं, लेकिन जेनेरिक विधि पैरामीटर नहीं।

सुपर: जब आप एक GenericClass<? super Bar> घोषित, आप कह रहे हैं "मैं क्या इसके जेनेरिक प्रकार तर्क वास्तव में नहीं पता है, लेकिन यह Bar की एक सुपर क्लास है।" अब विचार करें कि विधि आमंत्रण के लिए इसका क्या अर्थ है।

आप जानते हैं कि आपको वास्तव में क्या मिला है या तो GenericClass<Bar> या GenericClass<Baz> है। T लौटने वाली विधि पर विचार करें। पूर्व मामले में, यह Bar देता है। बाद में, Baz। यदि यह Baz देता है, तो उस मान को Bar चर पर असाइन करना एक त्रुटि है। आप नहीं जानते कि यह कौन सा है, इसलिए आप सुरक्षित रूप से यहां कुछ भी नहीं मान सकते हैं।

एक ऐसी विधि पर विचार करें जिसमें T पैरामीटर है। यदि यह GenericClass<Bar> है, तो इसे Bar पास करना कानूनी है। यदि यह GenericClass<Baz> है, तो इसे Bar पास करना अभी भी कानूनी है क्योंकि BarBaz का उप प्रकार है।

तो, निचले बाउंड के साथ आप जेनेरिक विधि पैरामीटर का उपयोग कर सकते हैं, लेकिन सामान्य वापसी मान नहीं।

सारांश में: <? extends T> का अर्थ है कि आप जेनेरिक रिटर्न वैल्यू का उपयोग कर सकते हैं लेकिन पैरामीटर नहीं। <? super T> का मतलब है कि आप जेनेरिक पैरामीटर का उपयोग कर सकते हैं लेकिन मान वापस नहीं कर सकते हैं। Predicate.test() में एक सामान्य पैरामीटर है, इसलिए आपको super की आवश्यकता है।

विचार करने की एक और बात: वाइल्डकार्ड द्वारा दी गई सीमाएं वस्तु के वास्तविक प्रकार के तर्क के बारे में हैं। के साथ उपयोग करने वाले प्रकारों पर उनके परिणाम विपरीत हैं। एक ऊपरी बाउंड वाइल्डकार्ड (extends) उन चर के प्रकारों पर निचला बाध्य है जो आप वापसी मान असाइन कर सकते हैं। निचले बाउंड वाइल्डकार्ड (super) पैरामीटर के रूप में आप जिन प्रकारों को पारित कर सकते हैं उन पर ऊपरी सीमा है। predicate.test(new Object()) संकलित नहीं होगा क्योंकि String की निचली सीमा के साथ, यह केवल String के उप-वर्ग स्वीकार करेगा।

+0

आपकी अंतर्दृष्टि के लिए धन्यवाद। वापसी प्रकार/पैरामीटर संक्षेपण उनके उपयोग को समझना बहुत आसान बनाता है। – piper1970

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