2014-11-11 20 views
19

मैं ऐसा करने की कोशिश कर रहा हूं जो कार्यात्मक प्रोग्रामिंग की नई जेडीके 8 भूमि में अपेक्षाकृत मूल बात है लेकिन यह काम नहीं कर सकता है।जावा लैम्ब्डा एक लैम्ब्डा लौट रहा है

import java.util.*; 
import java.util.concurrent.*; 
import java.util.stream.*; 

public class so1 { 
    public static void main() { 
     List<Number> l = new ArrayList<>(Arrays.asList(1, 2, 3)); 
     List<Callable<Object>> checks = l.stream(). 
       map(n -> (Callable<Object>)() -> { 
        System.out.println(n); 
        return null; 
       }). 
       collect(Collectors.toList()); 
    } 
} 

यह नंबरों की सूची लेता है और है कि उन्हें प्रिंट कर सकते हैं कार्यों की एक सूची: मैं इस काम कर कोड है। हालांकि कॉल करने योग्य के लिए स्पष्ट कलाकार अनावश्यक लगता है। यह मुझे और IntelliJ के लिए प्रतीत होता है। और हम दोनों का मानना ​​है कि यह भी काम करना चाहिए:

List<Callable<Object>> checks = l.stream(). 
     map(n ->() -> { 
      System.out.println(n); 
      return null; 
     }). 
     collect(Collectors.toList()); 

हालांकि मैं कोई त्रुटि मिलती है:

so1.java:10: error: incompatible types: cannot infer type-variable(s) R 
     List<Callable<Object>> checks = l.stream().map(n ->() -> {System.out.println(n); return null;}).collect(Collectors.toList()); 
                ^
    (argument mismatch; bad return type in lambda expression 
     Object is not a functional interface) 
    where R,T are type-variables: 
    R extends Object declared in method <R>map(Function<? super T,? extends R>) 
    T extends Object declared in interface Stream 
1 error 
+8

कास्ट, 'मानचित्र (..)' के लिए एक स्पष्ट प्रकार तर्क तर्क पसंद करते हैं। 'l.stream()। <कॉल करने योग्य > मानचित्र (...)' –

+4

संबंधित: http://stackoverflow.com/questions/24794924/generic-type-inference-not-working-with-method-Chain – assylias

उत्तर

20

आपने जावा 8 के लक्ष्य टाइपिंग की सीमा को मारा जो रिसीवर पर एक विधि आमंत्रण के लिए लागू होता है। जबकि लक्ष्य टाइपिंग पैरामीटर प्रकारों के लिए (ज्यादातर बार) काम करता है, यह ऑब्जेक्ट या अभिव्यक्ति के लिए काम नहीं करता है जिस पर आप विधि का आह्वान करते हैं।

यहां, l.stream(). map(n ->() -> { System.out.println(n); return null; })collect(Collectors.toList()) विधि आमंत्रण का प्राप्तकर्ता है, इसलिए लक्ष्य प्रकार List<Callable<Object>> इसके लिए विचार नहीं किया जाता है।

यह साबित करना आसान है कि लक्ष्य प्रकार पता है, तो नेस्टेड लैम्ब्डा अभिव्यक्तियां काम करती हैं, उदा।

static <T> Function<T,Callable<Object>> toCallable() { 
    return n ->() -> { 
     System.out.println(n); 
     return null; 
    }; 
} 
समस्याओं के बिना

काम करता है और आप अपने मूल समस्या को हल करने के लिए इसका इस्तेमाल कर सकते हैं के रूप में

List<Callable<Object>> checks = l.stream() 
    .map(toCallable()).collect(Collectors.toList()); 

तुम भी एक सहायक विधि है जो विधि रिसीवर से पहली अभिव्यक्ति की भूमिका में परिवर्तन शुरू करने से समस्या का समाधान कर सकते हैं एक पैरामीटर के लिए

// turns the Stream s from receiver to a parameter 
static <T, R, A> R collect(Stream<T> s, Collector<? super T, A, R> collector) { 
    return s.collect(collector); 
} 

और

के रूप में मूल अभिव्यक्ति पुनर्लेखन
List<Callable<Object>> checks = collect(l.stream().map(
    n ->() -> { 
     System.out.println(n); 
     return null; 
    }), Collectors.toList()); 

यह कोड की जटिलता को कम नहीं करता है लेकिन बिना किसी समस्या के संकलित किया जा सकता है। मेरे लिए, यह एक डीजा vu है। जब जावा 5 और जेनेरिक बाहर आए, प्रोग्रामर को new अभिव्यक्तियों पर प्रकार पैरामीटर दोहराना पड़ा, जबकि अभिव्यक्ति को सामान्य विधि में लपेटने के दौरान साबित हुआ कि प्रकार का उल्लंघन करना कोई समस्या नहीं है। यह जावा 7 तक लिया गया था जब प्रोग्रामर को टाइप तर्कों ("हीरा ऑपरेटर" का उपयोग करके) के इन अनावश्यक पुनरावृत्ति को छोड़ने की अनुमति दी गई थी।अब हमारे पास एक समान स्थिति है, जो किसी अन्य विधि में एक आमंत्रण अभिव्यक्ति को लपेटती है, रिसीवर को पैरामीटर में बदलती है, साबित करती है कि यह सीमा अनावश्यक है। तो शायद हम जावा 10 में इस सीमा से छुटकारा पाने के ...

+0

यह शायद मुझे यह समझने में कुछ हफ्तों लगेगा लेकिन मुझे लगता है कि मैं स्वीकार करूंगा;) –

2

मैं अभी तक कैसे प्रकार निष्कर्ष lambdas के साथ काम करता लिए सटीक नियमों में delved है। एक सामान्य भाषा डिजाइन दृष्टिकोण से, हालांकि, भाषा नियमों को लिखना हमेशा संभव नहीं होता है जो संकलक को जो भी लगता है उसे समझने की अनुमति देता है। मैं एक एडा-भाषा संकलक के लिए एक कंपाइलर रखरखाव रहा हूं और मैं वहां कई भाषा डिजाइन मुद्दों से परिचित हूं। एडा कई मामलों में टाइप अनुमान का उपयोग करता है (जहां निर्माण के प्रकार को पूरे अभिव्यक्ति को देखे बिना निर्धारित किया जा सकता है, जो मुझे लगता है कि इस जावा लैम्ब्डा अभिव्यक्ति के साथ भी मामला है)। कुछ भाषा नियम हैं जो संकलकों को अस्पष्ट के रूप में कुछ अभिव्यक्तियों को अस्वीकार करने का कारण बनते हैं, सिद्धांत रूप में, वास्तव में केवल एक ही संभावित व्याख्या होती है। एक कारण, अगर मैं सही ढंग से याद करता हूं, तो क्या किसी को ऐसा कोई मामला मिला है जहां एक नियम जो संकलक को सही व्याख्या के बारे में बताता है, उसे संकलक को सही ढंग से व्याख्या करने के लिए अभिव्यक्ति के माध्यम से 17 पास करने की आवश्यकता होती।

तो जब हम सोच सकते हैं कि एक कंपाइलर किसी विशेष मामले में कुछ समझने में सक्षम होना चाहिए, तो यह केवल सादा हो सकता है।

2

मैं इस एक ही मुद्दे में भाग गया और स्पष्ट रूप से सामान्य प्रकार पैरामीटर तो जैसे map को निर्दिष्ट करने के द्वारा इसे हल करने में सक्षम था: एक के बजाय

List<Callable<Object>> checks = l.stream(). 
    <Callable<Object>>map(n ->() -> { 
     System.out.println(n); 
     return null; 
    }). 
    collect(Collectors.toList()); 
संबंधित मुद्दे