जावा 8 में नए java.util.Arrays विधियां क्यों हैं सभी आदिम प्रकारों के लिए अधिभारित नहीं?



java-8 (1)

पूरी तरह से प्रश्नों को हल करने के लिए, न केवल यह विशेष परिदृश्य, मुझे लगता है कि हम सभी जानना चाहते हैं ....

जावा 8 में इंटरफ़ेस प्रदूषण क्यों है

उदाहरण के लिए, सी # जैसी भाषा में, पूर्वनिर्धारित फ़ंक्शन प्रकारों का एक सेट होता है जो किसी वैकल्पिक रिटर्न प्रकार के साथ किसी भी प्रकार के तर्क स्वीकार करते हैं ( Func और Action प्रत्येक व्यक्ति विभिन्न प्रकार के T1 , T2 , T3 , के 16 पैरामीटर तक जा रहा है ... , T16 ), लेकिन जेडीके 8 में हमारे पास अलग- अलग नाम और विभिन्न विधि नामों के साथ विभिन्न कार्यात्मक इंटरफेस का एक सेट है, और जिनकी अमूर्त विधियां अच्छी तरह से ज्ञात कार्य धर्मार्थियों (यानी शून्य, यूनरी, बाइनरी, टर्नरी, आदि)। और फिर हमारे पास आदिम प्रकार से निपटने वाले मामलों का विस्फोट होता है, और यहां तक ​​कि अन्य परिदृश्य भी होते हैं जो अधिक कार्यात्मक इंटरफेस के विस्फोट का कारण बनते हैं।

प्रकार मिटा समस्या

इसलिए, एक तरह से, दोनों भाषाओं में अंतरफलक प्रदूषण के कुछ रूप (या सी # में प्रतिनिधि प्रदूषण) से पीड़ित हैं। केवल अंतर यह है कि सी # में उनके सभी का एक ही नाम है। जावा में, दुर्भाग्य से, टाइप एरर के कारण, Function<T1,T2> और Function<T1,T2,T3> या Function<T1,T2,T3,...Tn> बीच कोई अंतर नहीं है, तो जाहिर है, हम नहीं कर सकते उन्हें बस उसी तरह नामित न करें और हमें सभी संभव प्रकार के फ़ंक्शन संयोजनों के लिए रचनात्मक नामों के साथ आना पड़ा।

ऐसा मत सोचो कि विशेषज्ञ समूह ने इस समस्या से संघर्ष नहीं किया है। लैम्बडा मेलिंग सूची में ब्रायन गोएट्ज़ के शब्दों में:

[...] एक उदाहरण के रूप में, चलो कार्य प्रकार लेते हैं। Devoxx में दी गई भेड़ का बच्चा स्ट्रॉमैन समारोह प्रकार था। मैंने जोर दिया कि हम उन्हें हटा दें, और इससे मुझे अलोकप्रिय बना दिया गया। लेकिन फ़ंक्शन प्रकारों पर मेरा आपत्ति यह नहीं था कि मुझे फ़ंक्शन प्रकार पसंद नहीं हैं - मुझे फ़ंक्शन प्रकार पसंद हैं - लेकिन उस प्रकार के फ़ंक्शन प्रकार जावा प्रकार सिस्टम के मौजूदा पहलू से खराब तरीके से लड़े। मिटाए गए फ़ंक्शन प्रकार दोनों दुनिया के सबसे खराब हैं। तो हमने इसे डिजाइन से हटा दिया।

लेकिन मैं यह कहने के लिए तैयार नहीं हूं कि "जावा में फ़ंक्शन प्रकार कभी नहीं होंगे" (हालांकि मुझे पता है कि जावा में कभी भी फ़ंक्शन प्रकार नहीं हो सकते हैं।) मेरा मानना ​​है कि फ़ंक्शन प्रकारों को प्राप्त करने के लिए, हमें पहले मिरर से निपटना होगा। यह संभव हो सकता है, या हो सकता है। लेकिन संशोधित संरचनात्मक प्रकारों की दुनिया में, फ़ंक्शन प्रकार बहुत अधिक समझने लगते हैं [...]

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

हालांकि, इन इंटरफेस को याद रखना अधिक कठिन हो जाता है क्योंकि उनके पास अलग-अलग नाम और तरीके हैं।

फिर भी, मैं उन लोगों में से एक हूं जो सोचते हैं कि उन्होंने स्केल में समस्या का समाधान क्यों नहीं किया, Function0 , Function1 , Function2 , ..., FunctionN एन जैसे इंटरफेस को परिभाषित किया। शायद, एकमात्र तर्क जो मैं इसके खिलाफ आ सकता हूं वह यह है कि वे एपीआई के पिछले संस्करणों में इंटरफेस के लिए लैम्ब्डा अभिव्यक्तियों को परिभाषित करने की संभावनाओं को अधिकतम करना चाहते थे जैसा कि पहले उल्लेख किया गया है।

मूल्य प्रकार के मुद्दे की कमी

तो, स्पष्ट रूप से टाइप एरर एक ड्राइविंग बल है। लेकिन यदि आप उन लोगों में से एक हैं जो हमें आश्चर्यजनक नामों और विधि हस्ताक्षरों के साथ इन सभी अतिरिक्त कार्यात्मक इंटरफेस की भी आवश्यकता है और जिसका एकमात्र अंतर एक आदिम प्रकार का उपयोग है, तो मुझे आपको याद दिलाना चाहिए कि जावा में हमें मूल्य प्रकारों की कमी भी है सी # जैसी भाषा में हैं। इसका मतलब है कि हमारे सामान्य वर्गों में उपयोग किए जाने वाले सामान्य प्रकार केवल संदर्भ प्रकार हो सकते हैं, न कि प्राचीन प्रकार।

दूसरे शब्दों में, हम यह नहीं कर सकते हैं:

List<int> numbers = asList(1,2,3,4,5);

लेकिन हम वास्तव में ऐसा कर सकते हैं:

List<Integer> numbers = asList(1,2,3,4,5);

दूसरा उदाहरण, हालांकि, मुक्केबाजी की लागत और लपेटी हुई वस्तुओं के अनबॉक्सिंग को आदि से लेकर आदिम प्रकार तक ले जाता है। यह आदिम मूल्यों के संग्रह से निपटने वाले संचालन में वास्तव में महंगा हो सकता है। इसलिए, विशेषज्ञ समूह ने विभिन्न परिदृश्यों से निपटने के लिए इंटरफेस के इस विस्फोट को बनाने का फैसला किया। चीजों को "कम बुरा" बनाने के लिए उन्होंने केवल तीन मूल प्रकारों से निपटने का फैसला किया: int, लंबा और डबल।

लैम्बडा मेलिंग सूची में ब्रायन गोएट्ज़ के शब्दों का हवाला देते हुए:

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

इसे और खराब नहीं करने के लिए ट्रिक # 1 है: हम सभी आठ आदिम प्रकार नहीं कर रहे हैं। हम int, लंबा, और डबल कर रहे हैं; इन सभी को इनके द्वारा अनुकरण किया जा सकता है। तर्कसंगत रूप से हम भी int से छुटकारा पा सकते हैं, लेकिन हमें नहीं लगता कि अधिकांश जावा डेवलपर इसके लिए तैयार हैं। हां, चरित्र के लिए कॉल की जाएगी, और जवाब "इसे एक int में चिपकाएं" है। (प्रत्येक विशेषज्ञता जेआरई पदचिह्न के लिए ~ 100K के लिए अनुमानित है।)

ट्रिक # 2 है: हम प्राचीन डोमेन (सॉर्टिंग, कमी) में सर्वोत्तम चीजों का पर्दाफाश करने के लिए आदिम धाराओं का उपयोग कर रहे हैं लेकिन बॉक्स किए गए डोमेन में जो कुछ भी कर सकते हैं उसे डुप्लिकेट करने का प्रयास नहीं कर रहे हैं। उदाहरण के लिए, कोई IntStream.into () नहीं है, जैसा कि Aleksey बताता है। (यदि वहां थे, तो अगला प्रश्न "इंटोलॉलेक्शन कहां है? IntArrayList? IntConcurrentSkipListMap?) इरादा है कि कई धाराएं संदर्भ धाराओं के रूप में शुरू हो सकती हैं और आदिम धाराओं के रूप में समाप्त हो सकती हैं, लेकिन इसके विपरीत नहीं। यह ठीक है, और आवश्यक रूपांतरणों की संख्या को कम कर देता है (उदाहरण के लिए, int -> टी के लिए मानचित्र का कोई अधिभार नहीं, int -> टी, आदि के लिए फ़ंक्शन का कोई विशेषज्ञता नहीं है) [...]

हम देख सकते हैं कि यह विशेषज्ञ समूह के लिए एक कठिन निर्णय था। मुझे लगता है कि कुछ इस बात से सहमत होंगे कि यह अच्छा है, और हम में से अधिकतर सहमत होंगे कि यह आवश्यक था।

चेक किए गए अपवाद मुद्दे

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

Writer out = new StringWriter();
Consumer<String> printer = s -> out.write(s); //oops! compiler error

ऐसा नहीं किया जा सकता क्योंकि write ऑपरेशन एक चेक अपवाद (यानी IOException ) फेंकता है लेकिन Consumer विधि का हस्ताक्षर यह घोषित नहीं करता है कि यह किसी भी अपवाद को फेंकता है। इसलिए, इस समस्या का एकमात्र समाधान और भी इंटरफेस बनाने के लिए होता है, कुछ अपवादों को घोषित करते हैं और कुछ नहीं (या अपवाद पारदर्शिता के लिए भाषा स्तर पर एक और तंत्र के साथ आते हैं। फिर, चीजों को "कम खराब" करने के लिए विशेषज्ञ समूह ने इस मामले में कुछ भी करने का फैसला किया।

लैम्बडा मेलिंग सूची में ब्रायन गोएट्ज़ के शब्दों में:

[...] हां, आपको अपने असाधारण एसएएम प्रदान करना होगा। लेकिन फिर लैम्ब्डा रूपांतरण उनके साथ ठीक काम करेगा।

ईजी ने इस समस्या के लिए अतिरिक्त भाषा और लाइब्रेरी समर्थन पर चर्चा की, और अंत में महसूस किया कि यह एक खराब लागत / लाभ व्यापार था।

लाइब्रेरी-आधारित समाधान एसएएम प्रकारों (असाधारण बनाम नहीं) में 2x विस्फोट का कारण बनते हैं, जो प्राचीन विशेषज्ञता के लिए मौजूदा संयोजी विस्फोटों के साथ बुरी तरह से बातचीत करते हैं।

उपलब्ध भाषा-आधारित समाधान जटिलता / मूल्य व्यापार से हार गए थे। यद्यपि कुछ वैकल्पिक समाधान हैं, लेकिन हम अन्वेषण जारी रखने जा रहे हैं - हालांकि स्पष्ट रूप से 8 के लिए नहीं और शायद 9 के लिए नहीं।

इस बीच, आपके पास जो भी आप चाहते हैं उसे करने के लिए टूल हैं। मुझे लगता है कि आप पसंद करते हैं कि हम आपके लिए उस अंतिम मील प्रदान करते हैं (और, दूसरी बात, आपका अनुरोध वास्तव में "आप पहले से ही चेक अपवादों को क्यों छोड़ नहीं देते" के लिए एक पतला-छिपी अनुरोध है, लेकिन मुझे लगता है कि वर्तमान स्थिति देता है आप अपना काम पूरा करते हैं। [...]

इसलिए, यह हमारे लिए है, डेवलपर्स, केस-दर-मामले आधार पर इनसे निपटने के लिए और भी अधिक इंटरफ़ेस विस्फोटों को तैयार करने के लिए:

interface IOConsumer<T> {
   void accept(T t) throws IOException;
}

static<T> Consumer<T> exceptionWrappingBlock(IOConsumer<T> b) {
   return e -> {
    try { b.accept(e); }
    catch (Exception ex) { throw new RuntimeException(ex); }
   };
}

आदेश के अनुसार:

Writer out = new StringWriter();
Consumer<String> printer = exceptionWrappingBlock(s -> out.write(s));

शायद, भविष्य में (शायद जेडीके 9) जब हमें जावा और रिएफिकेशन में वैल्यू टाइप के लिए समर्थन मिलता है, तो हम इन एकाधिक इंटरफेस में से कुछ से छुटकारा पाने में सक्षम होंगे (या कम से कम अब और उपयोग करने की आवश्यकता नहीं है)।

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

मैं जावा 8 के लिए एपीआई परिवर्तनों की समीक्षा कर रहा हूं और मैंने देखा है कि java.util.Arrays में नए तरीकों को सभी प्राइमेटिव्स के लिए ओवरलोड नहीं किया गया है। मैंने देखा तरीकों हैं:

वर्तमान में ये नई विधियां केवल int , long , और double primitives को संभालती हैं।

int , long , और double शायद सबसे व्यापक रूप से उपयोग किए जाने वाले प्राइमेटिव हैं इसलिए यह समझ में आता है कि अगर उन्हें एपीआई को सीमित करना होता है तो वे उन तीनों को चुनते हैं, लेकिन उन्हें एपीआई को सीमित क्यों करना पड़ा?





java-8