पैराओमर के वास्तविक प्रकार के आधार पर ओवरलोड विधि चयन


115

मैं इस कोड के साथ प्रयोग कर रहा हूं:

interface Callee {
    public void foo(Object o);
    public void foo(String s);
    public void foo(Integer i);
}

class CalleeImpl implements Callee
    public void foo(Object o) {
        logger.debug("foo(Object o)");
    }

    public void foo(String s) {
        logger.debug("foo(\"" + s + "\")");
    }

    public void foo(Integer i) {
        logger.debug("foo(" + i + ")");
    }
}

Callee callee = new CalleeImpl();

Object i = new Integer(12);
Object s = "foobar";
Object o = new Object();

callee.foo(i);
callee.foo(s);
callee.foo(o);

यह foo(Object o)तीन बार प्रिंट करता है। मुझे उम्मीद है कि विधि चयन वास्तविक (घोषित नहीं) पैरामीटर प्रकार को ध्यान में रखेगा। क्या मैं कुछ भूल रहा हूँ? क्या इस कोड को संशोधित करने का कोई तरीका है ताकि यह प्रिंट हो जाए foo(12), foo("foobar")और foo(Object o)?

जवाबों:


96

मैं वास्तविक (घोषित नहीं) पैरामीटर प्रकार को ध्यान में रखते हुए विधि चयन की उम्मीद करता हूं। क्या मैं कुछ भूल रहा हूँ?

हाँ। आपकी अपेक्षा गलत है। जावा में, गतिशील विधि प्रेषण केवल उस वस्तु के लिए होता है जिसे विधि कहा जाता है, अतिभारित तरीकों के पैरामीटर प्रकारों के लिए नहीं।

जावा भाषा विशिष्टता का हवाला देते हुए :

जब कोई विधि लागू की जाती है (.1215.12), वास्तविक तर्कों की संख्या (और किसी भी स्पष्ट प्रकार के तर्क) और संकलित समय के प्रकारों का उपयोग किया जाता है, संकलित समय पर, उस विधि के हस्ताक्षर को निर्धारित करने के लिए, जिसे लागू किया जाएगा ( §15.12.2)। यदि जो विधि लागू की जानी है वह एक उदाहरण विधि है, तो आमंत्रित की जाने वाली वास्तविक विधि को गतिशील विधि लुकअप (method15.12.4) का उपयोग करके, रन टाइम पर निर्धारित किया जाएगा।


4
क्या आप कृपया उद्धृत किए गए युक्ति की व्याख्या कर सकते हैं। दोनों वाक्य एक दूसरे के विपरीत प्रतीत होते हैं। ऊपर दिया गया उदाहरण उदाहरण के तरीकों का उपयोग करता है, फिर भी जो विधि लागू की जा रही है वह स्पष्ट रूप से रन टाइम पर निर्धारित नहीं की जा रही है।
एलेक्स वर्डेन

15
@ एलेक्स वर्डेन: इस मामले में, विधि के मापदंडों के संकलन समय का उपयोग विधि के हस्ताक्षर को निर्धारित करने के लिए किया जाता है foo(Object)। रनटाइम के दौरान, ऑब्जेक्ट विधि के वर्ग को यह निर्धारित किया जाता है कि उस पद्धति के कार्यान्वयन को कहा जाता है, इस बात को ध्यान में रखते हुए कि यह घोषित प्रकार के एक उपवर्ग का एक उदाहरण हो सकता है जो विधि को ओवरराइड करता है।
माइकल बोर्गवर्ड

86

जैसा कि ओवरलोडिंग संकल्प से पहले उल्लेख किया गया है संकलन समय पर किया जाता है।

जावा गूढ़ व्यक्ति इसके लिए एक अच्छा उदाहरण है:

पहेली 46: कन्फ्यूजिंग कंस्ट्रक्टर का मामला

यह पहेली आपको दो कन्फ्यूजिंग कंस्ट्रक्टर के साथ प्रस्तुत करती है। मुख्य विधि एक निर्माता को आमंत्रित करती है, लेकिन कौन सा? कार्यक्रम का आउटपुट उत्तर पर निर्भर करता है। प्रोग्राम क्या प्रिंट करता है, या क्या यह कानूनी है?

public class Confusing {

    private Confusing(Object o) {
        System.out.println("Object");
    }

    private Confusing(double[] dArray) {
        System.out.println("double array");
    }

    public static void main(String[] args) {
        new Confusing(null);
    }
}

समाधान 46: कन्फ्यूजिंग कंस्ट्रक्टर का मामला

... जावा की अधिभार संकल्प प्रक्रिया दो चरणों में संचालित होती है। पहला चरण उन सभी तरीकों या निर्माणकर्ताओं का चयन करता है जो सुलभ और लागू होते हैं। दूसरा चरण पहले चरण में चुने गए तरीकों या निर्माणकर्ताओं में से सबसे विशिष्ट का चयन करता है । एक विधि या कंस्ट्रक्टर दूसरे की तुलना में कम विशिष्ट है अगर यह किसी भी अन्य को पारित मापदंडों को स्वीकार कर सकता है [जेएलएस 15.12.2.5]।

हमारे कार्यक्रम में, दोनों निर्माणकर्ता सुलभ और लागू हैं। कंस्ट्रक्टर कन्फ्यूज़िंग (ऑब्जेक्ट) कन्फ़्यूज़िंग (डबल []) को दिए गए किसी भी पैरामीटर को स्वीकार करता है , इसलिए कन्फ़्यूज़िंग (ऑब्जेक्ट) कम विशिष्ट है। (हर एक डबल ऐरे एक ऑब्जेक्ट है , लेकिन हर ऑब्जेक्ट एक डबल ऐरे नहीं है ।) सबसे विशिष्ट कंस्ट्रक्टर इसलिए कन्फ्यूजिंग (डबल []) है , जो प्रोग्राम के आउटपुट को समझाता है।

यदि आप टाइप डबल [] का मान पास करते हैं तो यह व्यवहार समझ में आता है ; यदि आप शून्य से गुजरते हैं तो यह काउंटरिंटिटिव है । इस पहेली को समझने की कुंजी यह है कि किस विधि या निर्माता के लिए परीक्षण सबसे विशिष्ट है, वास्तविक मापदंडों का उपयोग नहीं करता है : आह्वान में दिखाई देने वाले पैरामीटर। उनका उपयोग केवल यह निर्धारित करने के लिए किया जाता है कि कौन से ओवरलोडिंग लागू हैं। एक बार कंपाइलर यह निर्धारित करता है कि कौन सा ओवरलोडिंग लागू और सुलभ है, यह केवल औपचारिक मापदंडों का उपयोग करके सबसे विशिष्ट ओवरलोडिंग का चयन करता है: घोषणा में प्रदर्शित होने वाले पैरामीटर।

एक अशक्त पैरामीटर के साथ कन्फ्यूज़िंग (ऑब्जेक्ट) कंस्ट्रक्टर को आमंत्रित करने के लिए , नया कन्फ़्यूज़िंग (ऑब्जेक्ट) नल लिखें । यह सुनिश्चित करता है कि केवल कन्फ्यूज़िंग (ऑब्जेक्ट) लागू हो। अधिक आम तौर पर, संकलक को एक विशिष्ट ओवरलोडिंग का चयन करने के लिए मजबूर करने के लिए, वास्तविक मापदंडों को औपचारिक प्रकारों के घोषित प्रकारों में डालना।


4
मुझे आशा है कि इसके कहने में देर नहीं हुई - "एसओएफ पर सर्वश्रेष्ठ व्याख्याओं में से एक"। धन्यवाद :)
TheLostMind

5
मेरा मानना ​​है कि अगर हमने कंस्ट्रक्टर के निजी कन्फ्यूजिंग (int [] iArray) को भी जोड़ा, तो यह संकलन करने में विफल होगा, क्या ऐसा नहीं होगा? क्योंकि अब एक ही विशिष्टता वाले दो निर्माता हैं।
रिसर

यदि मैं फ़ंक्शन इनपुट के रूप में डायनामिक रिटर्न प्रकार का उपयोग करता हूं, तो यह हमेशा कम विशिष्ट का उपयोग करता है ... उस पद्धति का उपयोग सभी संभावित रिटर्न मानों के लिए किया जा सकता है ...
kaiser

16

विभिन्न प्रकार के तर्कों के आधार पर एक कॉल को भेजने की क्षमता को कई प्रेषण कहा जाता है । जावा में यह विज़िटर पैटर्न के साथ किया जाता है

हालाँकि, जब से आप Integerएस और Stringएस के साथ काम कर रहे हैं , आप आसानी से इस पैटर्न को शामिल नहीं कर सकते हैं (आप इन कक्षाओं को संशोधित नहीं कर सकते हैं)। इस प्रकार, switchऑब्जेक्ट रन-टाइम पर एक विशाल आपकी पसंद का हथियार होगा।


11

जावा में कॉल करने की विधि (जिस पद्धति में हस्ताक्षर का उपयोग करना है) को संकलन समय पर निर्धारित किया जाता है, इसलिए यह संकलन समय प्रकार के साथ जाता है।

इसके आसपास काम करने के लिए विशिष्ट पैटर्न ऑब्जेक्ट हस्ताक्षर के साथ विधि में ऑब्जेक्ट प्रकार की जांच करना और एक डाली के साथ विधि को सौंपना है।

    public void foo(Object o) {
        if (o instanceof String) foo((String) o);
        if (o instanceof Integer) foo((Integer) o);
        logger.debug("foo(Object o)");
    }

यदि आपके पास कई प्रकार हैं और यह असहनीय है, तो विधि अधिभार शायद सही दृष्टिकोण नहीं है, बल्कि सार्वजनिक विधि को केवल ऑब्जेक्ट लेना चाहिए और ऑब्जेक्ट प्रकार के अनुसार उचित हैंडलिंग को सौंपने के लिए किसी प्रकार की रणनीति पैटर्न को लागू करना चाहिए।


4

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

मैंने जिस समाधान का उपयोग किया वह प्रतिबिंब के माध्यम से निर्माणकर्ता को देखना था ...

public Parameter[] convertObjectsToParameters(Object[] objArray) {
    Parameter[] paramArray = new Parameter[objArray.length];
    int i = 0;
    for (Object obj : objArray) {
        try {
            Constructor<Parameter> cons = Parameter.class.getConstructor(obj.getClass());
            paramArray[i++] = cons.newInstance(obj);
        } catch (Exception e) {
            throw new IllegalArgumentException("This method can't handle objects of type: " + obj.getClass(), e);
        }
    }
    return paramArray;
}

कोई बदसूरत Instof, स्विच स्टेटमेंट या विज़िटर पैटर्न की आवश्यकता नहीं है! :)


2

जावा किस प्रकार कॉल करने की विधि निर्धारित करने का प्रयास करते समय संदर्भ प्रकार को देखता है। यदि आप अपने कोड को 'सही' विधि चुनना चाहते हैं, तो आप अपने खेतों को विशिष्ट प्रकार के उदाहरणों के रूप में घोषित कर सकते हैं:

Integeri = new Integer(12);
String s = "foobar";
Object o = new Object();

आप अपने परमों को परम के प्रकार के रूप में भी डाल सकते हैं:

callee.foo(i);
callee.foo((String)s);
callee.foo(((Integer)o);

1

यदि विधि कॉल में निर्दिष्ट संख्याओं और तर्कों के बीच एक सटीक मिलान है और एक अतिभारित विधि के विधि हस्ताक्षर हैं, तो वह विधि है जिसे लागू किया जाएगा। आप ऑब्जेक्ट संदर्भ का उपयोग कर रहे हैं, इसलिए जावा संकलन समय पर निर्णय लेता है कि ऑब्जेक्ट परम के लिए, एक विधि है जो सीधे ऑब्जेक्ट को स्वीकार करती है। तो इसने उस विधि को 3 बार कहा।

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