यह जावा 8 लंबोदर संकलन करने में विफल क्यों है?


85

निम्नलिखित जावा कोड संकलित करने में विफल रहता है:

@FunctionalInterface
private interface BiConsumer<A, B> {
    void accept(A a, B b);
}

private static void takeBiConsumer(BiConsumer<String, String> bc) { }

public static void main(String[] args) {
    takeBiConsumer((String s1, String s2) -> new String("hi")); // OK
    takeBiConsumer((String s1, String s2) -> "hi"); // Error
}

संकलक रिपोर्ट:

Error:(31, 58) java: incompatible types: bad return type in lambda expression
    java.lang.String cannot be converted to void

अजीब बात यह है कि चिह्नित लाइन "ओके" ठीक संकलित करती है, लेकिन चिह्नित लाइन "त्रुटि" विफल हो जाती है। वे अनिवार्य रूप से समान लगते हैं।


5
क्या यह टाइपो है यहां कार्यात्मक इंटरफ़ेस विधि शून्य है?
नाथन ह्यूजेस

6
@ नथनहुगेस नोप। यह प्रश्न का केंद्र है- स्वीकृत उत्तर देखें।
ब्रायन गॉर्डन

वहाँ के अंदर कोड होना चाहिए { }की takeBiConsumer... और यदि ऐसा है तो, आपको एक उदाहरण देता सकता है ... अगर मैं इस सही ढंग से पढ़ा है, bcवर्ग / इंटरफेस का एक उदाहरण है BiConsumer, और इस तरह एक विधि कहा जाता है को शामिल करना चाहिए acceptइंटरफ़ेस हस्ताक्षर से मेल करने के लिए। .. ... और अगर यह सही है, तो acceptविधि को कहीं न कहीं परिभाषित किया जाना चाहिए (जैसे एक वर्ग जो इंटरफ़ेस को लागू करता है) ... तो यह है कि क्या होना चाहिए {}?? ... ... ... धन्यवाद
dsdsdsdsd

एक विधि के साथ इंटरफेस जावा 8 में लैम्ब्डा के साथ विनिमेय हैं। इस मामले में, (String s1, String s2) -> "hi"BiConsumer <स्ट्रिंग, स्ट्रिंग> का एक उदाहरण है।
ब्रायन गॉर्डन

जवाबों:


100

आपके लंबोदर को बधाई देने की जरूरत है BiConsumer<String, String>। यदि आप JLS # 15.27.3 (लैम्बडा का प्रकार) देखें :

एक लंबोदर अभिव्यक्ति एक फ़ंक्शन प्रकार के साथ अनुरूप है यदि निम्न में से सभी सत्य हैं:

  • [...]
  • यदि फ़ंक्शन प्रकार का परिणाम शून्य है, तो लैम्ब्डा बॉडी या तो स्टेटमेंट एक्सप्रेशन (.814.8) या शून्य-संगत ब्लॉक है।

तो लैम्बडा या तो एक स्टेटमेंट एक्सप्रेशन होना चाहिए या एक शून्य संगत ब्लॉक:


31
@BrianGordon एक स्ट्रिंग शाब्दिक एक अभिव्यक्ति (सटीक होने के लिए एक स्थिर अभिव्यक्ति) है, लेकिन एक बयान अभिव्यक्ति नहीं है।
assylias

44

मूल रूप से, new String("hi")कोड का एक निष्पादन योग्य टुकड़ा है जो वास्तव में कुछ करता है (यह एक नया स्ट्रिंग बनाता है और फिर इसे वापस करता है)। लौटाए गए मान को अनदेखा किया जा सकता है और new String("hi")फिर भी एक नया स्ट्रिंग बनाने के लिए शून्य-रिटर्न लैंबडा में उपयोग किया जा सकता है।

हालाँकि, "hi"बस एक स्थिर है जो अपने आप पर कुछ भी नहीं करता है। लैम्ब्डा शरीर में इसके साथ करने के लिए केवल उचित चीज इसे वापस करना है। लेकिन लैम्ब्डा विधि में रिटर्न टाइप Stringया Object, लेकिन यह रिटर्न होता है void, इसलिए String cannot be casted to voidत्रुटि।


6
सही औपचारिक शब्द एक्सप्रेशन स्टेटमेंट है , एक उदाहरण निर्माण अभिव्यक्ति दोनों स्थानों पर दिखाई दे सकती है, जहां एक अभिव्यक्ति या जहां एक बयान की आवश्यकता होती है, जबकि एक Stringशाब्दिक सिर्फ एक अभिव्यक्ति है जिसे बयान संदर्भ में उपयोग नहीं किया जा सकता है।
होल्गर

2
स्वीकृत उत्तर औपचारिक रूप से सही हो सकता है, लेकिन यह एक बेहतर स्पष्टीकरण है
edc65

3
@ edc65: यही कारण है कि इस उत्तर को भी उत्थान मिला। नियमों के लिए तर्क और गैर-औपचारिक सहज स्पष्टीकरण वास्तव में मदद कर सकते हैं, हालांकि, प्रत्येक प्रोग्रामर को यह पता होना चाहिए कि इसके पीछे औपचारिक नियम हैं और इस मामले में कि औपचारिक नियम का परिणाम सहज रूप से समझ में नहीं आता है, औपचारिक नियम अभी भी जीत जाएगा । जैसे ()->x++कानूनी है, जबकि ()->(x++), मूल रूप से बिल्कुल वैसा ही कर रहा है, ... नहीं है
होलगर

21

पहला मामला ठीक है क्योंकि आप एक "विशेष" विधि (एक निर्माता) का आह्वान कर रहे हैं और आप वास्तव में बनाई गई वस्तु को नहीं ले रहे हैं। बस इसे और अधिक स्पष्ट करने के लिए, मैं आपके लैंबडास में वैकल्पिक ब्रेसिज़ लगाऊंगा:

takeBiConsumer((String s1, String s2) -> {new String("hi");}); // OK
takeBiConsumer((String s1, String s2) -> {"hi"}); // Error

और अधिक स्पष्ट, मैं पुराने संकेतन में अनुवाद करूंगा:

takeBiConsumer(new BiConsumer<String, String>(String s1, String s2) {
    public void accept(String s, String s2) {
        new String("hi"); // OK
    }
});

takeBiConsumer(new BiConsumer<String, String>(String s1, String s2) {
    public void accept(String s, String s2) {
        "hi"; // Here, the compiler will attempt to add a "return"
              // keyword before the "hi", but then it will fail
              // with "compiler error ... bla bla ...
              //  java.lang.String cannot be converted to void"
    }
});

पहले मामले में आप एक कंस्ट्रक्टर को निष्पादित कर रहे हैं, लेकिन आप बनाए गए ऑब्जेक्ट को वापस नहीं कर रहे हैं, दूसरे मामले में आप स्ट्रिंग मान वापस करने का प्रयास कर रहे हैं, लेकिन आपके इंटरफ़ेस में आपका तरीका BiConsumerशून्य है, इसलिए कंपाइलर त्रुटि देता है।


12

JLS निर्दिष्ट करते हैं कि

यदि फ़ंक्शन प्रकार का परिणाम शून्य है, तो लैम्ब्डा बॉडी या तो स्टेटमेंट एक्सप्रेशन (.814.8) या शून्य-संगत ब्लॉक है।

अब देखते हैं कि विस्तार से,

चूंकि आपकी takeBiConsumerविधि शून्य प्रकार की है, इसलिए प्राप्त लंबोदर new String("hi")इसे एक ब्लॉक की तरह व्याख्या करेगा

{
    new String("hi");
}

जो एक शून्य में मान्य है, इसलिए पहला मामला संकलन है।

हालांकि, उस मामले में जहां लंबोदर है -> "hi", जैसे एक ब्लॉक

{
    "hi";
}

जावा में सिंटैक्स मान्य नहीं है। इसलिए "हाय" करने के लिए केवल एक चीज की कोशिश करना और उसे वापस करना है।

{
    return "hi";
}

जो एक शून्य में मान्य नहीं है और त्रुटि संदेश की व्याख्या करता है

incompatible types: bad return type in lambda expression
    java.lang.String cannot be converted to void

बेहतर समझ के लिए, ध्यान दें कि यदि आप takeBiConsumerस्ट्रिंग के प्रकार को बदलते हैं , -> "hi"तो मान्य होगा क्योंकि यह सीधे स्ट्रिंग को वापस करने की कोशिश करेगा।


ध्यान दें कि सबसे पहले मैंने सोचा था कि त्रुटि लंबोदा के गलत संदर्भ के कारण हुई थी, इसलिए मैं इस संभावना को समुदाय के साथ साझा करूंगा:

जेएलएस 15.27

यह एक संकलित समय त्रुटि है अगर एक लैम्बडा अभिव्यक्ति एक कार्यक्रम में असाइनमेंट संदर्भ (,5.2), एक मंगलाचरण संदर्भ (§5.3), या एक कास्टिंग संदर्भ (§5.5) के अलावा किसी अन्य स्थान पर होती है।

हालाँकि हमारे मामले में, हम एक आह्वान के संदर्भ में हैं जो सही है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.