java - जावा 8 इंटरफ़ेस विधियों में "अंतिम" क्यों अनुमति नहीं है?




language-design java-8 (3)

@EJP से टिप्पणियों में उल्लिखित राक्षसों के लिए "उत्तर" को ढूंढना और पहचानना मुश्किल होगा: दुनिया में लगभग 2 (+/- 2) लोग हैं जो निश्चित उत्तर दे सकते हैं। और संदेह में, उत्तर कुछ ऐसा हो सकता है जैसे "अंतिम डिफ़ॉल्ट विधियों का समर्थन आंतरिक कॉल रिज़ॉल्यूशन तंत्र को पुनर्गठन के प्रयास के लायक नहीं लगता"। यह निश्चित रूप से अनुमान है, लेकिन ओपनजेडीके मेलिंग सूची में इस कथन (दो व्यक्तियों में से एक) की तरह कम से कम सूक्ष्म साक्ष्य का समर्थन किया जाता है:

"मुझे लगता है कि अगर" अंतिम डिफ़ॉल्ट "विधियों की अनुमति है, तो उन्हें आंतरिक दृश्यमान से उपयोगकर्ता-दृश्य invokeinterface में पुनर्लेखन की आवश्यकता हो सकती है।"

और इस तरह के मामूली तथ्यों को एक विधि (वास्तव में) अंतिम विधि माना जाता है जब यह एक default विधि है, वर्तमान में Method::is_final_method में Method::is_final_method विधि में लागू किया गया है।

इसके अलावा वास्तव में "आधिकारिक" जानकारी अत्यधिक वेबसेच के साथ और प्रतिबद्ध लॉग पढ़ने के साथ भी खोजना मुश्किल है। मैंने सोचा कि यह invokeinterface निर्देश और क्लास विधि कॉल के साथ इंटरफ़ेस विधि कॉल के संकल्प के दौरान संभावित अस्पष्टताओं से संबंधित हो सकता है, invokeinterface निर्देश के अनुरूप: invokevirtual निर्देश के लिए, एक साधारण vtable लुकअप हो सकता है, क्योंकि विधि जरूरी है या तो सुपरक्लास से विरासत में प्राप्त किया जाता है, या सीधे कक्षा द्वारा लागू किया जाता है। इसके विपरीत, एक invokeinterface कॉल को यह पता लगाने के लिए संबंधित कॉल साइट की जांच करनी चाहिए कि यह कॉल वास्तव में किस इंटरफेस को संदर्भित करता है (यह हॉटस्पॉट विकी के इंटरफेस कॉल पेज में अधिक विस्तार से समझाया गया है)। हालांकि, final विधियों को या तो vtable में सम्मिलित नहीं किया जाता है, या vtable में मौजूदा प्रविष्टियों को प्रतिस्थापित करें ( klassVtable.cpp देखें । लाइन 333 ), और इसी तरह, डिफ़ॉल्ट विधियां vtable में मौजूदा प्रविष्टियों को प्रतिस्थापित कर रही हैं ( klassVtable.cpp देखें , रेखा 202 )। तो वास्तविक कारण (और इस प्रकार, उत्तर) को (बल्कि जटिल) विधि कॉल रिज़ॉल्यूशन तंत्र के अंदर गहराई से छुपाया जाना चाहिए, लेकिन हो सकता है कि इन संदर्भों को फिर भी सहायक के रूप में माना जाएगा, चाहे वे वास्तविक उत्तर प्राप्त करें उसमें से।

जावा 8 की सबसे उपयोगी सुविधाओं में से एक इंटरफ़ेस पर नई default विधियां हैं। अनिवार्य रूप से दो कारण हैं (अन्य भी हो सकते हैं) क्यों उन्हें पेश किया गया है:

  • वास्तविक डिफ़ॉल्ट कार्यान्वयन प्रदान करना। उदाहरण: Iterator.remove()
  • जेडीके एपीआई विकास के लिए अनुमति। उदाहरण: Iterable.forEach()

एक एपीआई डिजाइनर के परिप्रेक्ष्य से, मुझे इंटरफ़ेस विधियों, जैसे final पर अन्य संशोधक का उपयोग करने में सक्षम होना पसंद होता। सुविधा विधियों को जोड़ते समय यह उपयोगी होगा, कक्षाओं को कार्यान्वित करने में "आकस्मिक" ओवरराइड को रोकना:

interface Sender {

    // Convenience method to send an empty message
    default final void send() {
        send(null);
    }

    // Implementations should only implement this method
    void send(String message);
}

अगर Sender एक वर्ग था तो उपर्युक्त पहले से ही सामान्य अभ्यास है:

abstract class Sender {

    // Convenience method to send an empty message
    final void send() {
        send(null);
    }

    // Implementations should only implement this method
    abstract void send(String message);
}

अब, default और final स्पष्ट रूप से कीवर्ड का विरोधाभास कर रहे हैं, लेकिन डिफ़ॉल्ट कीवर्ड को सख्ती से जरूरी नहीं किया गया था , इसलिए मुझे लगता है कि यह विरोधाभास जानबूझकर है, "शरीर के साथ वर्ग विधियों" (केवल विधियों) के बीच सूक्ष्म मतभेदों को प्रतिबिंबित करने के लिए और "शरीर के साथ इंटरफ़ेस विधियां" (डिफ़ॉल्ट विधियां), यानी अंतर जो मैंने अभी तक नहीं समझा है।

कुछ समय पर, ब्रायन गोएट्ज का हवाला देते हुए इंटरफ़ेस विधियों पर static और final जैसे संशोधक के लिए समर्थन अभी तक पूरी तरह से अन्वेषण नहीं किया गया था:

दूसरा हिस्सा यह है कि हम इंटरफेस में क्लास-बिल्डिंग टूल्स का समर्थन करने के लिए कितने दूर जा रहे हैं, जैसे अंतिम तरीके, निजी तरीके, संरक्षित तरीके, स्थैतिक विधियां इत्यादि। जवाब है: हम अभी तक नहीं जानते

2011 के आखिर में उस समय से, स्पष्ट रूप से, इंटरफेस में static तरीकों के लिए समर्थन जोड़ा गया था। जाहिर है, इसने जेडीके पुस्तकालयों के लिए बहुत अधिक मूल्य जोड़ा, जैसे Comparator.comparing()

सवाल:

final कारण (और static final ) ने जावा 8 इंटरफेस को कभी नहीं बनाया है?


मुझे नहीं लगता कि एक दृढ़ता इंटरफ़ेस विधि पर final निर्दिष्ट करने के लिए यह आवश्यक नहीं है, मैं सहमत हूं कि यह सहायक हो सकता है, लेकिन प्रतीत होता है कि लागतों से लाभ कम हो गया है।

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

कोई भी ऐसा Collection लिख सकता है जो इंटरफ़ेस का पालन करता है और फिर उन विधियों में चीजें करता है जो पूरी तरह से अंतर्ज्ञानी हैं, व्यापक यूनिट परीक्षण लिखने के अलावा, स्वयं को ढालने का कोई तरीका नहीं है।


जावा 8 इंटरफ़ेस विधियों में "सिंक्रनाइज़" की अनुमति क्यों है, इस कारण से कुछ हद तक यह सवाल है ?

डिफ़ॉल्ट तरीकों के बारे में समझने की मुख्य बात यह है कि प्राथमिक डिजाइन लक्ष्य इंटरफ़ेस विकास है , न कि "मध्यस्थता (मध्यस्थ) लक्षणों को चालू करें"। जबकि दोनों के बीच कुछ ओवरलैप है, और हमने बाद वाले लोगों के साथ रहने की कोशिश की जहां यह पूर्व के रास्ते में नहीं आया, इस प्रकाश में देखे जाने पर इन प्रश्नों को सबसे अच्छी तरह समझा जाता है। (ध्यान दें कि इंटरफ़ेस विधियों को विरासत में गुणा किया जा सकता है, इस तथ्य के आधार पर, वर्ग विधियों इंटरफ़ेस विधियों से अलग होने जा रहे हैं , इससे कोई फर्क नहीं पड़ता।)

डिफ़ॉल्ट विधि का मूल विचार यह है: यह एक डिफ़ॉल्ट कार्यान्वयन के साथ एक इंटरफेस विधि है, और एक व्युत्पन्न वर्ग अधिक विशिष्ट कार्यान्वयन प्रदान कर सकता है। और क्योंकि डिज़ाइन सेंटर इंटरफ़ेस विकास था, यह एक महत्वपूर्ण डिज़ाइन लक्ष्य था कि स्रोत-संगत और बाइनरी-संगत तरीके से तथ्य के बाद डिफ़ॉल्ट विधियों को इंटरफ़ेस में जोड़ा जा सकता है।

"अंतिम डिफ़ॉल्ट विधियों क्यों नहीं" का बहुत आसान जवाब यह है कि तब शरीर केवल डिफ़ॉल्ट कार्यान्वयन नहीं होगा, यह एकमात्र कार्यान्वयन होगा। हालांकि यह एक छोटा सा जवाब है, यह हमें एक सुराग देता है कि सवाल पहले से ही एक संदिग्ध दिशा में जा रहा है।

एक और कारण है कि अंतिम इंटरफ़ेस विधियां संदिग्ध हैं कि वे कार्यान्वयनकर्ताओं के लिए असंभव समस्याएं पैदा करते हैं। उदाहरण के लिए, आपको लगता है कि आपके पास है:

interface A { 
    default void foo() { ... }
}

interface B { 
}

class C implements A, B { 
}

यहां, सबकुछ अच्छा है; सी ए से foo () विरासत में आता है अब बी को लगता है कि एक डिफ़ॉल्ट विधि के साथ एक foo विधि है:

interface B { 
    default void foo() { ... }
}

अब, जब हम सी को पुन: संकलित करने के लिए जाते हैं, तो संकलक हमें बताएगा कि यह नहीं जानता कि foo() लिए कौन सा व्यवहार प्राप्त करना है, इसलिए सी को इसे ओवरराइड करना है (और A.super.foo() को प्रतिनिधि चुनना चुन सकता है यह वही व्यवहार बनाए रखना चाहता था।) लेकिन क्या होगा अगर बी ने अपना डिफ़ॉल्ट फाइनल बनाया हो, और ए सी के लेखक के नियंत्रण में नहीं है? अब सी अप्रत्याशित रूप से टूटा हुआ है; यह foo() को ओवरराइड किए बिना संकलित नहीं कर सकता है, लेकिन यह B में अंतिम होने पर foo() ओवरराइड नहीं कर सकता है।

यह सिर्फ एक उदाहरण है, लेकिन मुद्दा यह है कि अंतिम तरीके वास्तव में एक उपकरण है जो एकल विरासत वर्गों (आमतौर पर जो कुछ राज्य व्यवहार के लिए) की दुनिया में अधिक समझ में आता है, इंटरफेस की तुलना में जो केवल व्यवहार का योगदान करता है और विरासत में गुणा किया जा सकता है । "अंतिम कार्यान्वयनकर्ता में अन्य इंटरफेस को मिश्रित किया जा सकता है" के बारे में तर्क करना बहुत मुश्किल है, और एक इंटरफेस विधि को अंतिम होने की इजाजत देने से इन समस्याओं का कारण बन सकता है (और वे इंटरफ़ेस लिखने वाले व्यक्ति पर नहीं उड़ाएंगे, लेकिन खराब उपयोगकर्ता जो इसे लागू करने का प्रयास करता है।)

उन्हें अस्वीकार करने का एक अन्य कारण यह है कि उनका मतलब यह नहीं होगा कि उनका क्या मतलब है। एक डिफ़ॉल्ट कार्यान्वयन केवल तभी माना जाता है जब वर्ग (या इसके सुपरक्लास) विधि की घोषणा (ठोस या सार) प्रदान नहीं करते हैं। यदि कोई डिफ़ॉल्ट विधि अंतिम थी, लेकिन एक सुपरक्लास पहले ही विधि को कार्यान्वित कर दिया गया था, तो डिफॉल्ट को अनदेखा कर दिया जाएगा, जो शायद अंतिम लेखक घोषित करते समय डिफ़ॉल्ट लेखक की अपेक्षा नहीं कर रहा था। (यह विरासत व्यवहार डिफॉल्ट विधियों के लिए डिज़ाइन सेंटर का प्रतिबिंब है - इंटरफ़ेस विकास। मौजूदा इंटरफ़ेस में एक डिफ़ॉल्ट विधि (या किसी मौजूदा इंटरफ़ेस विधि के लिए डिफ़ॉल्ट कार्यान्वयन) को जोड़ना संभव है, जो पहले से ही कार्यान्वित किए बिना, कार्यान्वित किए बिना इंटरफ़ेस को लागू करने वाले मौजूदा वर्गों का व्यवहार, यह गारंटी देता है कि डिफ़ॉल्ट विधियों से पहले पहले से काम करने वाले वर्गों को डिफ़ॉल्ट तरीकों की उपस्थिति में वैसे ही काम किया जाएगा।)







jsr335