2010-03-26 9 views
21

जावा varargs कार्यान्वयन में एक बग प्रतीत होता है। विभिन्न प्रकार के vararg पैरामीटर के साथ विधि ओवरलोड होने पर जावा उपयुक्त प्रकार को अलग नहीं कर सकता है।varargs और overloading के साथ बग?

यह मुझे एक त्रुटि The method ... is ambiguous for the type ...

पर विचार करें निम्नलिखित कोड देता है:

public class Test 
{ 
    public static void main(String[] args) throws Throwable 
    { 
     doit(new int[]{1, 2}); // <- no problem 
     doit(new double[]{1.2, 2.2}); // <- no problem 
     doit(1.2f, 2.2f); // <- no problem 
     doit(1.2d, 2.2d); // <- no problem 
     doit(1, 2); // <- The method doit(double[]) is ambiguous for the type Test 
    } 

    public static void doit(double... ds) 
    { 
     System.out.println("doubles"); 
    } 

    public static void doit(int... is) 
    { 
     System.out.println("ints"); 
    } 
} 

docs कहते हैं: "सामान्यतया, आप एक varargs विधि ओवरलोड नहीं करना चाहिए, या यह प्रोग्रामर के लिए मुश्किल हो जाएगा पता लगाएं कि ओवरलोडिंग कहलाती है। "

हालांकि वे इस त्रुटि का उल्लेख नहीं करते हैं, और यह प्रोग्रामर नहीं है जो इसे मुश्किल लग रहे हैं, यह संकलक है।

विचार?

संपादित - संकलक: सूर्य JDK 1.6.0 U18

+13

हो सकता है कि सौभाग्य से, इस समस्या से बचने के एक जोड़े को अलग तरीके हैं वे प्रोग्रामर के बारे में बात कर रहे हैं जिन्होंने संकलक लिखा था। – mob

+1

क्या संस्करण और संकलक? ग्रहण या जेडीके? –

+0

संपादित देखें - सूर्य जेडीके 1.6.0 u18 – pstanton

उत्तर

7

at the Sun Forums से अधिक इस बारे में एक चर्चा है।

कोई असली रिज़ॉल्यूशन, बस इस्तीफा नहीं।

वरर्ग (और ऑटो-मुक्केबाजी, जो विशेष रूप से वर्गार्ग के साथ संयोजन में कठोर व्यवहार का कारण बनती है) को बाद में जावा के जीवन में बोल्ड किया गया है, और यह एक ऐसा क्षेत्र है जहां यह दिखाता है। तो यह कंपाइलर की तुलना में, spec में एक और बग है।

कम से कम, यह अच्छा (?) एससीजेपी चाल प्रश्नों के लिए बनाता है।

+5

असल में, इसे एक कंपाइलर बग के रूप में स्वीकार किया जाता है, और बग अब तय कर दिया गया है। –

+0

मुझे लगता है कि प्रश्न में बग वह है जिसे आप यहां देख सकते हैं: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6199075 –

13

समस्या यह है कि यह अस्पष्ट है।

doIt(1, 2); 

doIt(int ...), या doIt(double ...) के लिए एक कॉल हो सकता है। बाद के मामले में, पूर्णांक अक्षर को double मानों पर पदोन्नत किया जाएगा।

मुझे पूरा यकीन है कि जावा स्पेक कहता है कि यह एक संदिग्ध निर्माण है, और संकलक सिर्फ spec द्वारा निर्धारित नियमों का पालन कर रहा है। (मुझे यकीन है कि हो सकता है यह आगे अनुसंधान करने के लिए होगा।)

संपादित - JLS के प्रासंगिक हिस्सा है "15.12.2.5 Choosing the Most Specific Method" है, लेकिन यह मेरे सिर चोट कर रही है।

मुझे लगता है कि तर्क हो सकता है कि कि void doIt(int[]) अधिक विशिष्ट (या इसके विपरीत) void doIt(double[]) से नहीं है क्योंकि int[] की double[] (और इसके विपरीत) एक उप प्रकार नहीं है। चूंकि दो ओवरलोड समान रूप से विशिष्ट हैं, इसलिए कॉल संदिग्ध है।

इसके विपरीत, void doItAgain(int) से void doItAgain(double) क्योंकि int अनुसार JLS double की एक उप-प्रकार है और अधिक विशिष्ट है। इसलिए, doItAgain(42) पर कॉल संदिग्ध नहीं है।

संपादित 2 - @finnw सही है, यह एक बग है। 15.12.2.5 के इस हिस्से पर विचार करें (गैर लागू मामलों को दूर करने के संपादित):

एक चर arity सदस्य विधि नामित मीटर अधिक विशिष्ट एक ही नाम के एक और चर arity सदस्य विधि की तुलना में है अगर:

एक सदस्य विधि में एन पैरामीटर हैं और दूसरे के के पैरामीटर हैं, जहां n ≥ k। पहली सदस्य विधि के पैरामीटर के प्रकार टी 1 हैं,। । । , टीएन -1, टीएन [], अन्य विधि के पैरामीटर के प्रकार यू 1 हैं,। । । , ब्रिटेन -1, ब्रिटेन []। चलो Si = उई, 1 < = i < = के।तब:

    1 से सभी j के लिए
  • K-1 के लिए, टी जे <: Sj, और,
  • सभी j के लिए कश्मीर से n करने के लिए, टी जे <: Sk

इस लागू करें उस मामले में जहां n = k = 1, और हम देखते हैं कि doIt(int[])doIt(double[]) से अधिक विशिष्ट है।


वास्तव में, वहाँ इस के लिए एक bug report है और सूर्य मानता है कि यह वास्तव में एक बग है, हालांकि वे के रूप में "बहुत कम" यह प्राथमिकता के आधार पर किया है। बग अब जावा 7 (बी 123) में फिक्स्ड के रूप में चिह्नित है।

+3

उस तर्क से, doIt (int) और doIt (डबल) अस्पष्ट होना चाहिए (varargs के बिना)। – Thilo

+2

यह varargs के साथ बातचीत है जो इस संदिग्ध, आईआईआरसी बनाता है। आपके पास वास्तव में पदोन्नति के दो स्तर चल रहे हैं, 1) एक सरणी के लिए तर्क के अनुक्रम का प्रचार, और 2) युगल के अभिन्न अंगों का प्रचार। –

+2

मुझे लगता है कि यह एक बग है (यानी कंपाइलर व्यवहार जेएलएस के साथ संगत नहीं है।) यदि मैं जेएलएस के उस अनुभाग को सही ढंग से समझता हूं तो 'doIt (int ...)' को 'dIt (डबल। 'से सख्ती से अधिक विशिष्ट होना चाहिए। ..) 'क्योंकि 'int'' डबल' का उचित उप प्रकार है। यह सच है कि 'int [] '' डबल [] 'का उप-प्रकार नहीं है, लेकिन यह आवश्यकताओं में से एक नहीं है इसलिए इसे ओवरलोड रिज़ॉल्यूशन को प्रभावित नहीं करना चाहिए। – finnw

4

दिलचस्प।

आप विधि हस्ताक्षर में बजाय आवरण प्रकार का उपयोग कर सकते हैं::

public static void doit(Double... ds) { 
     for(Double currD : ds) { 
      System.out.println(currD); 
     } 
    } 

    public static void doit(Integer... is) { 
     for(Integer currI : is) { 
      System.out.println(currI); 
     } 
    } 

या, आप जेनरिक का उपयोग कर सकते हैं:

public static <T> void doit(T... ts) { 
     for(T currT : ts) { 
     System.out.println(currT); 
     } 
    } 
+2

आप जेनिक्स का उपयोग नहीं कर सकते, क्योंकि तब आपके पास केवल एक ही विधि है। संभवतः, कोड इट्स के मुकाबले युगल के लिए कुछ और करता है। – Thilo

+0

अच्छा बिंदु। मैं हस्ताक्षर में रैपर प्रकारों का उपयोग कर चिपके रहूंगा: :) –

+2

दिलचस्प हैक। यह काम करता है क्योंकि ऑटो-मुक्केबाजी और पदोन्नति दोनों एक ही तर्क पर लागू नहीं होती हैं, इसलिए ऑटो-मुक्केबाजी को मजबूर करने से आप अंतर-टू-डबल प्रचार को रोकते हैं और केवल एक विधि हस्ताक्षर मिलान करते हैं। यह * नहीं * उदाहरण # 3 के लिए काम करेगा ('फ्लोट' मानों के साथ।) – finnw