जावा ternary ऑपरेटर बनाम अगर / में <JDK8 संगतता


113

हाल ही में मैं स्प्रिंग फ्रेमवर्क का सोर्स कोड पढ़ रहा हूं। कुछ मैं समझ नहीं पा रहा हूँ यहाँ जाता है:

public Member getMember() {
    // NOTE: no ternary expression to retain JDK <8 compatibility even when using
    // the JDK 8 compiler (potentially selecting java.lang.reflect.Executable
    // as common type, with that new base class not available on older JDKs)
    if (this.method != null) {
        return this.method;
    }
    else {
        return this.constructor;
    }
}

यह विधि वर्ग का सदस्य है org.springframework.core.MethodParameter। कोड समझना आसान है जबकि टिप्पणियां कठिन हैं।

नोट: JDK 8 कंपाइलर का उपयोग करते समय भी JDK <8 संगतता बनाए रखने के लिए कोई तिर्यक अभिव्यक्ति नहीं है (संभवतः java.lang.reflect.Executableसामान्य प्रकार के रूप में चयन करने के साथ, पुराने JDK पर नया बेस क्लास उपलब्ध नहीं है)

if...else...इस संदर्भ में टर्नरी अभिव्यक्ति का उपयोग करने और निर्माण के बीच अंतर क्या है?

जवाबों:


103

जब आप ऑपरेंड के प्रकार के बारे में सोचते हैं, तो समस्या और अधिक स्पष्ट हो जाती है:

this.method != null ? this.method : this.constructor

दोनों ऑपरेंड के सबसे विशेष प्रकार के प्रकार के रूप में है, यानी दोनों के लिए सबसे विशेष प्रकार के आम this.methodऔर this.constructor

जावा 7 में यह है java.lang.reflect.Member, हालांकि जावा 8 क्लास लाइब्रेरी एक नए प्रकार का परिचय java.lang.reflect.Executableदेती है जो सामान्य से अधिक विशिष्ट है Member। इसलिए जावा 8 क्लास लाइब्रेरी के साथ परिणामी प्रकार की टर्नरी अभिव्यक्ति के Executableबजाय है Member

जावा 8 कंपाइलर के कुछ (प्री-रिलीज़) संस्करणों से लगता है Executableकि टर्नरी ऑपरेटर को संकलित करते समय अंदर के उत्पन्न कोड में एक स्पष्ट संदर्भ उत्पन्न होता है। यह एक क्लास लोड को ट्रिगर करता है, और इस प्रकार ClassNotFoundExceptionक्लास लाइब्रेरी <JDK 8 के साथ चलने पर रनटाइम को चालू करता है , क्योंकि Executableकेवल JDK। 8 के लिए ही मौजूद है।

जैसा कि इस उत्तर में टैगिर वलेव ने उल्लेख किया है , यह वास्तव में जेडीके 8 के पूर्व-रिलीज़ संस्करणों में एक बग है और तब से तय किया गया है, इसलिए दोनों if-elseवर्कअराउंड और व्याख्यात्मक टिप्पणी अब अप्रचलित हैं।

अतिरिक्त नोट: कोई इस निष्कर्ष पर आ सकता है कि यह कंपाइलर बग जावा 8 से पहले मौजूद था। हालांकि, ओपनजेडके 7 द्वारा बनाए गए बाइट कोड के लिए बाइट कोड ओपनजेडके 8 द्वारा उत्पन्न बाइट कोड के समान है। वास्तव में, इसका प्रकार अभिव्यक्ति रनटाइम पर पूरी तरह से अनियंत्रित हो जाती है, कोड वास्तव में केवल परीक्षण, शाखा, लोड, बिना किसी अतिरिक्त चेक के वापस जाने के लिए होता है। तो बाकी का आश्वासन दिया कि यह एक समस्या (अब) नहीं है और वास्तव में जावा 8 के विकास के दौरान एक अस्थायी समस्या है।


1
फिर JDK 1.8 के साथ कोड को JDK 1.7 पर कैसे संकलित किया जा सकता है। मुझे पता है कि कम संस्करण JDK के साथ संकलित कोड परेशानी के बिना उच्च संस्करण JDK पर चल सकता है।
jddxf

1
@jddxf जब तक आप उचित वर्ग फ़ाइल संस्करण निर्दिष्ट करते हैं तब तक सब कुछ ठीक रहता है और बाद के संस्करणों में उपलब्ध किसी भी कार्यक्षमता का उपयोग नहीं करते हैं। समस्या उत्पन्न होने के लिए बाध्य है, हालांकि अगर इस तरह का उपयोग इस मामले में अंतर्निहित रूप से होता है।
ढेक

13
@jddxf, उपयोग स्रोत / -target javac विकल्प
Tagir Valeev

1
आप सभी का धन्यवाद, विशेष रूप से ढेक और टैगिर वलेव, जिन्होंने पूरी तरह से स्पष्टीकरण दिया है
jddxf

30

आधिकारिक जेडीके -8 रिलीज से लगभग एक साल पहले 3 मई, 2013 को इसे काफी पुरानी प्रतिबद्धताओं में पेश किया गया था । कंपाइलर उन समय भारी विकास के अधीन था, इसलिए ऐसी संगतता समस्याएं हो सकती थीं। मुझे लगता है, स्प्रिंग टीम ने सिर्फ JDK-8 बिल्ड का परीक्षण किया और समस्याओं को ठीक करने की कोशिश की, हालांकि वे वास्तव में संकलक समस्याएं हैं। JDK-8 की आधिकारिक रिलीज से यह अप्रासंगिक हो गया। अब इस कोड में टर्नरी ऑपरेटर अपेक्षा के अनुरूप काम करता है ( Executableसंकलित .class-file में मौजूद क्लास का कोई संदर्भ नहीं है)।

वर्तमान में इसी तरह की चीजें JDK-9 में दिखाई देती हैं: कुछ कोड जो कि JDK-8 में अच्छी तरह से संकलित किए जा सकते हैं, JDK-9 javac के साथ विफल है। मुझे लगता है, रिलीज होने तक इस तरह की अधिकांश समस्याएं ठीक हो जाएंगी।


2
+1। तो, क्या यह शुरुआती कंपाइलर में बग था? क्या यह व्यवहार, जहां इसे संदर्भित किया गया था Executable, कल्पना के कुछ पहलू का उल्लंघन? या यह सिर्फ इतना है कि ओरेकल ने महसूस किया कि वे इस व्यवहार को इस तरह से बदल सकते हैं जो अभी भी कल्पना के अनुरूप होगा और बिना पिछड़े-अनुकूलता को तोड़ने के?
बरबाद

2
@ruakh, मुझे लगता है कि यह बग था। बाइटकोड में (या तो जावा -8 में या पूर्व में) यह स्पष्ट रूप से Executableइन-बीच टाइप करने के लिए पूरी तरह से अनावश्यक है । जावा -8 में अभिव्यक्ति प्रकार की धारणा की अवधारणा में भारी बदलाव आया और इस हिस्से को पूरी तरह से फिर से लिखा गया, इस प्रकार यह बहुत आश्चर्यजनक नहीं है कि शुरुआती कार्यान्वयन में बग थे।
टैगिर वलेव

7

मुख्य अंतर यह है कि एक if elseब्लॉक एक बयान है जबकि टर्नरी (अधिक बार जावा में सशर्त ऑपरेटर के रूप में जाना जाता है ) एक अभिव्यक्ति है

एक बयानreturn कुछ नियंत्रण मार्गों पर फोन करने वाले की तरह काम कर सकता है । एक असाइनमेंट में एक अभिव्यक्ति का उपयोग किया जा सकता है:

int n = condition ? 3 : 2;

तो हालत के बाद टर्नरी में दो अभिव्यक्तियाँ एक ही प्रकार के लिए ज़बरदस्त होनी चाहिए। यह विशेष रूप से ऑटो-बॉक्सिंग और स्वचालित संदर्भ कास्टिंग के साथ जावा में कुछ अजीब प्रभाव पैदा कर सकता है - यह वही है जो आपके पोस्ट किए गए कोड में टिप्पणी है। आपके मामले में अभिव्यक्तियों का बलाघात एक java.lang.reflect.Executableप्रकार का होगा (जैसा कि यह सबसे विशेष प्रकार है ) और यह जावा के पुराने संस्करणों में मौजूद नहीं है।

if elseयदि कोड स्टेटमेंट-जैसा है, और यदि यह अभिव्यक्ति-जैसा है तो एक टर्नरी है, तो स्टाइलिस्टली आपको एक ब्लॉक का उपयोग करना चाहिए ।

बेशक, if elseयदि आप एक लैम्ब्डा फ़ंक्शन का उपयोग करते हैं, तो आप एक ब्लॉक को एक अभिव्यक्ति की तरह व्यवहार कर सकते हैं ।


6

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

यह देखना मुश्किल है कि एक कास्ट क्यों नहीं लिखी गई।

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