टीएल; डीआर, यह एक कंपाइलर बग है।
ऐसा कोई नियम नहीं है जो विरासत में मिलने या तयशुदा तरीके से होने पर किसी विशेष लागू पद्धति को वरीयता दे। दिलचस्प है, जब मैं कोड को बदल देता हूं
interface ConsumerOne<T> {
void accept(T a);
}
interface ConsumerTwo<T> {
void accept(T a);
}
interface CustomIterable<T> extends Iterable<T> {
void forEach(ConsumerOne<? super T> c); //overload
void forEach(ConsumerTwo<? super T> c); //another overload
}
iterable.forEach((A a) -> aList.add(a));
बयान ग्रहण करने में कोई त्रुटि पैदा करता है।
चूंकि दूसरे अधिभार की घोषणा करते समय इंटरफ़ेस forEach(Consumer<? super T) c)
से विधि की कोई संपत्ति नहीं Iterable<T>
बदली, इसलिए विधि के किसी भी गुण के आधार पर इस विधि का चयन करने का ग्रहण का निर्णय (लगातार) नहीं हो सकता है। यह अभी भी केवल विरासत में मिली विधि है, अभी भी एकमात्र default
विधि है, अभी भी एकमात्र JDK विधि है, और इसी तरह। इन गुणों में से किसी को भी विधि चयन को प्रभावित नहीं करना चाहिए।
ध्यान दें कि घोषणा को बदलने के लिए
interface CustomIterable<T> {
void forEach(ConsumerOne<? super T> c);
default void forEach(ConsumerTwo<? super T> c) {}
}
एक "अस्पष्ट" त्रुटि भी पैदा करता है, इसलिए लागू ओवरलोड तरीकों की संख्या या तो मायने नहीं रखती है, यहां तक कि जब केवल दो उम्मीदवार होते हैं, तो default
तरीकों के लिए कोई सामान्य वरीयता नहीं होती है ।
अब तक, समस्या तब प्रकट होती है जब दो लागू विधियां और ए हैं default
विधि और एक वंशानुक्रम संबंध शामिल होता है, लेकिन आगे खुदाई करने के लिए यह सही जगह नहीं है।
लेकिन यह समझ में आता है कि आपके उदाहरण के निर्माण को संकलक में विभिन्न कार्यान्वयन कोड द्वारा नियंत्रित किया जा सकता है, एक बग प्रदर्शित करता है जबकि दूसरा नहीं करता है।
a -> aList.add(a)
एक स्पष्ट रूप से टाइप किया गया लैम्ब्डा अभिव्यक्ति है, जिसका उपयोग अधिभार संकल्प के लिए नहीं किया जा सकता है। इसके विपरीत, (A a) -> aList.add(a)
एक स्पष्ट रूप से टाइप की गई लैम्ब्डा अभिव्यक्ति है जिसका उपयोग ओवरलोड तरीकों से मिलान विधि का चयन करने के लिए किया जा सकता है, लेकिन यह यहां मदद नहीं करता है (यहां मदद नहीं करनी चाहिए), क्योंकि सभी विधियों में बिल्कुल कार्यात्मक हस्ताक्षर के साथ पैरामीटर प्रकार हैं ।
एक काउंटर-उदाहरण के रूप में, के साथ
static void forEach(Consumer<String> c) {}
static void forEach(Predicate<String> c) {}
{
forEach(s -> s.isEmpty());
forEach((String s) -> s.isEmpty());
}
कार्यात्मक हस्ताक्षर भिन्न होते हैं, और स्पष्ट रूप से टाइप किए गए लैम्ब्डा अभिव्यक्ति का उपयोग करना वास्तव में सही विधि का चयन करने में मदद कर सकता है जबकि अंतर्निहित टाइप किए गए लैम्ब्डा अभिव्यक्ति में मदद नहीं करता है, इसलिए forEach(s -> s.isEmpty())
एक कंपाइलर त्रुटि पैदा करता है। और सभी जावा कंपाइलर इसके बारे में सहमत हैं।
ध्यान दें कि aList::add
एक अस्पष्ट विधि संदर्भ है, क्योंकि add
विधि भी अतिभारित है, इसलिए यह एक विधि का चयन करने में भी मदद नहीं कर सकता है, लेकिन विधि संदर्भों को अलग-अलग कोड द्वारा संसाधित किया जा सकता है। एक अस्पष्ट aList::contains
या स्विच करने के List
लिए Collection
, add
अस्पष्ट बनाने के लिए स्विच करना , मेरे ग्रहण स्थापना (मैंने इस्तेमाल किया 2019-06
) में परिणाम नहीं बदला ।