जावा 7 में हीरा ऑपरेटर (<>) का क्या कहना है?


445

जावा 7 में हीरा ऑपरेटर निम्नलिखित की तरह कोड की अनुमति देता है:

List<String> list = new LinkedList<>();

हालाँकि जावा 5/6 में, मैं बस लिख सकता हूँ:

List<String> list = new LinkedList();

प्रकार के क्षरण के बारे में मेरी समझ यह है कि ये बिल्कुल समान हैं। (जेनेरिक वैसे भी रनटाइम पर हटा दिया जाता है)।

हीरे को लेकर परेशान क्यों? क्या नई कार्यक्षमता / प्रकार सुरक्षा यह अनुमति देता है? यदि यह कोई नई कार्यक्षमता उत्पन्न नहीं करता है तो वे इसे एक विशेषता के रूप में क्यों उल्लेख करते हैं? क्या इस अवधारणा की मेरी समझ त्रुटिपूर्ण है?


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

जब आप चेतावनी को अक्षम करते हैं तो यह वास्तव में बेकार है ... :) मेरे लिए
Renetik

3
इसका उत्तर दिया गया है, लेकिन यह जावा ट्यूटोरियल (पृष्ठ के मध्य के आसपास) में भी है: docs.oracle.com/javase/tutorial/java/generics/…
एंड्रियास तसौलस

DZone पर इस बारे में अच्छा लेख ।
आर। ओस्टरहॉल्ट

2
मेरा विचार यह है कि यह सूची के लिए वाक्यगत शर्करा है <स्ट्रींग> सूची = नया लिंक्डलिस्ट <जो बाईं ओर का प्रकार है> (); यानी इसे सामान्य रखते हुए
विक्टर मेलग्रेन

जवाबों:


496

के साथ मुद्दा

List<String> list = new LinkedList();

यह है कि बाएं हाथ की तरफ, आप जेनेरिक प्रकार का उपयोग कर रहे हैं List<String>जहां दाईं ओर आप कच्चे प्रकार का उपयोग कर रहे हैं LinkedList। जावा में कच्चे प्रकार केवल पूर्व-जेनरिक कोड के साथ संगतता के लिए प्रभावी रूप से मौजूद हैं और नए कोड में कभी भी उपयोग नहीं किया जाना चाहिए जब तक कि आपको पूरी तरह से नहीं करना पड़े।

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

जहाँ तक आपका मूल उदाहरण है List<String> list = new LinkedList(), संकलक उस असाइनमेंट के लिए एक चेतावनी उत्पन्न करता है क्योंकि यह होना चाहिए। इस पर विचार करो:

List<String> strings = ... // some list that contains some strings

// Totally legal since you used the raw type and lost all type checking!
List<Integer> integers = new LinkedList(strings);

गलत काम करने के खिलाफ संकलन-समय सुरक्षा प्रदान करने के लिए जेनरिक मौजूद हैं। उपरोक्त उदाहरण में, कच्चे प्रकार का उपयोग करने का मतलब है कि आपको यह सुरक्षा नहीं मिलती है और रनटाइम में एक त्रुटि मिलेगी। यही कारण है कि आपको कच्चे प्रकारों का उपयोग नहीं करना चाहिए।

// Not legal since the right side is actually generic!
List<Integer> integers = new LinkedList<>(strings);

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

मुझे लगता है कि समझने वाली महत्वपूर्ण बात यह है कि कच्चे प्रकार (नहीं के साथ <>) को सामान्य प्रकार के समान नहीं माना जा सकता है। जब आप एक कच्चे प्रकार की घोषणा करते हैं, तो आपको कोई भी लाभ नहीं मिलता है और जेनेरिक की जांच की जाती है। आपको यह भी ध्यान रखना होगा कि जेनेरिक जावा भाषा का एक सामान्य उद्देश्य हिस्सा है ... वे सिर्फ एस के नो-आर्ग कन्स्ट्रक्टर्स पर लागू नहीं होते हैं Collection!


31
पिछड़ी संगतता महान है, लेकिन कृपया जटिलता की कीमत पर नहीं। क्यों जावा 7 सिर्फ -compatibilityकंपाइलर स्विच नहीं लगा सकता है जबकि यदि अनुपस्थित है तो javacसभी कच्चे प्रकारों को मना करेगा और केवल सामान्य प्रकार के कड़ाई से लागू करेगा? यह हमारे कोड को कम क्रिया जैसा बना देगा।
रोजदी कासिम

3
@ रोसडी: सहमत, नए कोड में कच्चे प्रकार की कोई आवश्यकता नहीं है। हालाँकि, मैं जोर से स्रोत फ़ाइल (कमांड लाइन का उपयोग करके) के बजाय स्रोत फ़ाइल में जावा संस्करण संख्या सहित पसंद करूंगा। मेरा जवाब देखें।
Maaartinus

37
मैं व्यक्तिगत रूप से हीरे के उपयोग को पसंद नहीं करता हूं, जिसे आप परिभाषित कर रहे हैं और एक ही लाइन पर त्वरित कर रहे हैं। List<String> strings = new List<>()ठीक है, लेकिन अगर आप परिभाषित करते हैं private List<String> my list;, और फिर उस पृष्ठ को आधा कर देते हैं my_list = new List<>(), जिसके साथ आप तुरंत आते हैं , तो यह अच्छा नहीं है! मेरी सूची में फिर से क्या है? ओह, मुझे परिभाषा के लिए चारों ओर शिकार करने दें। अचानक, हीरे के शॉर्टकट का फायदा अलविदा हो जाता है।
रमीराबेल

11
@rmirabelle यह कैसे अलग है my_list = getTheList():? इस तरह की समस्याओं से निपटने के कई बेहतर तरीके हैं: 1. एक आईडीई का उपयोग करें जो आपको माउस होवर पर विभिन्न प्रकार के चर दिखाता है। 2. अधिक सार्थक चर नामों का उपयोग करें, जैसे कि private List<String> strings3. जब तक आपको वास्तव में नहीं करना है तब तक चर की घोषणा और विभाजन को विभाजित न करें।
नेटिक्स

1
@ मॉर्फिडन: हाँ, यह अभी भी जावा 8 के लिए वैध है। मुझे पूरा यकीन है कि आपको चेतावनी मिलनी चाहिए
कॉलिनड १D

36

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

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

उपयोग List<String> list = new LinkedList()करने से आपको कच्चे माल की चेतावनी मिलेगी।


8
List<String> list = new LinkedList()सही कोड है। आप यह जानते हैं और मैं यह भी जानता हूं। और सवाल (जैसा कि मैं इसे समझता हूं) है: केवल जावा कंपाइलर यह क्यों नहीं समझता कि यह कोड काफी सुरक्षित है?
रोमन

22
@Roman: List<String> list = new LinkedList()है सही कोड। यकीन है, अगर यह होता तो अच्छा होता! और यह शायद हो सकता था अगर जावा में शुरुआत से ही जेनेरिक था और जेनेरिक प्रकारों के पीछे की संगतता से निपटना नहीं था जो गैर-जेनेरिक हुआ करते थे, लेकिन यह करता है।
कॉलिनडी

6
@ColinD जावा को वास्तव में प्रत्येक एकल पंक्ति में पश्चगामी संगतता से निपटने की आवश्यकता नहीं है । जेनेरिक का उपयोग करते हुए किसी भी जावा स्रोत फ़ाइल में पुराने गैर-जेनेरिक प्रकारों को निषिद्ध किया जाना चाहिए (आप हमेशा उपयोग कर सकते हैं <?>यदि विरासत कोड में हस्तक्षेप हो) और बेकार हीरा ऑपरेटर मौजूद नहीं होना चाहिए।
माआर्टिनस

16

यह रेखा [अनियंत्रित] चेतावनी का कारण बनती है:

List<String> list = new LinkedList();

इसलिए, यह सवाल बदल जाता है: क्यों [अनियंत्रित] चेतावनी केवल मामले के लिए स्वचालित रूप से दबाया नहीं जाता है जब नया संग्रह बनाया जाता है?

मुझे लगता है, यह बहुत मुश्किल काम होगा तब <>फीचर जोड़ना ।

UPD : मुझे भी लगता है कि कानूनी तौर पर कच्चे माल का इस्तेमाल 'कुछ चीजों के लिए' करने पर गड़बड़ होगी।


13

सिद्धांत रूप में, हीरा ऑपरेटर आपको बार-बार प्रकार के तर्कों को सहेजकर अधिक कॉम्पैक्ट (और पठनीय) कोड लिखने की अनुमति देता है। व्यवहार में, यह सिर्फ दो भ्रामक आकर्षण है और आपको कुछ नहीं दे रहा है। क्यों?

  1. कोई भी संत प्रोग्रामर नए कोड में कच्चे प्रकार का उपयोग नहीं करता है। इसलिए कंपाइलर केवल यह मान सकता है कि कोई भी प्रकार के तर्क लिखकर आप यह नहीं चाहते हैं कि वह उनका अनुमान लगा सके।
  2. हीरा ऑपरेटर कोई प्रकार की जानकारी नहीं देता है, यह सिर्फ संकलक का कहना है, "यह ठीक रहेगा"। तो इसे छोड़ कर आप कोई नुकसान नहीं कर सकते। किसी भी स्थान पर जहां हीरा ऑपरेटर कानूनी है, वह संकलक द्वारा "अनुमान" किया जा सकता है।

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

Btw।, मुझे नहीं लगता कि यह एक संकलन स्विच का उपयोग करके किया जाना चाहिए। प्रोग्राम फ़ाइल का जावा संस्करण फ़ाइल की एक विशेषता है, कोई विकल्प नहीं है। के रूप में तुच्छ के रूप में कुछ का उपयोग कर

package 7 com.example;

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


2
आप के बीच के अंतर पर विचार किया है new ArrayList(anotherList)और new ArrayList<>(anotherList)(विशेष रूप से अगर यह करने के लिए आवंटित किया जा रहा है List<String>और anotherListएक है List<Integer>)?
पॉल बेलोरा

@ पाओल बेलोरा: नहीं, मेरे लिए, दोनों संकलन। हीरे के साथ किसी ने भी कोई चेतावनी नहीं दी। हालाँकि, मुझे इसमें कोई समझ नहीं है, क्या आप विस्तृत कर सकते हैं?
मारार्टिनस

क्षमा करें, मैंने बहुत अच्छा नहीं समझाया। इन दो उदाहरणों के बीच अंतर देखें: ideone.com/uyHagh और ideone.com/ANkg3T मैं केवल यह इंगित कर रहा हूं कि यह हीरे के ऑपरेटर को कच्चे प्रकार के बजाय उपयोग करने के लिए मायने रखता है, कम से कम जब सामान्य सीमा के साथ तर्क पारित किए जा रहे हैं में,
पॉल बेलोरा

वास्तव में मैंने कॉलिनड के उत्तर को पढ़ने के लिए समय नहीं लिया था - वह एक ही बात का हवाला देता है।
पॉल बेलोरा

2
इसलिए, यदि हम कच्चे प्रकारों के लिए एक नया वाक्यविन्यास पेश करने वाले हैं, तो कुछ जगहों के लिए जहां वास्तव में इसकी आवश्यकता है, क्यों न कुछ का उपयोग करें new @RawType List()। यह पहले से ही मान्य है जावा 8 सिंटैक्स और प्रकार एनोटेशन हर जगह पर इसका उपयोग करने की अनुमति देता है, जैसे कि जरूरत है @RawType List = (@RawType List) genericMethod();। यह देखते हुए कि कच्चे प्रकार वर्तमान में एक संकलक चेतावनी बनाते हैं जब तक कि एक उपयुक्त @SuppressWarningsस्थान नहीं रखा जाता है, @RawTypeएक उचित प्रतिस्थापन होगा और एक अधिक सूक्ष्म वाक्यविन्यास की आवश्यकता नहीं है।
होल्गर

8

जब आप लिखते हैं List<String> list = new LinkedList();, संकलक एक "अनियंत्रित" चेतावनी पैदा करता है। आप इसे नजरअंदाज कर सकते हैं, लेकिन अगर आप इन चेतावनियों को नजरअंदाज करते हैं तो आपको एक चेतावनी भी याद आ सकती है जो आपको वास्तविक प्रकार की सुरक्षा समस्या के बारे में सूचित करती है।

इसलिए, एक कोड लिखना बेहतर है जो अतिरिक्त चेतावनी उत्पन्न नहीं करता है, और हीरा ऑपरेटर आपको अनावश्यक पुनरावृत्ति के बिना सुविधाजनक तरीके से करने की अनुमति देता है।


4

अन्य प्रतिक्रियाओं में कहा गया सभी वैध हैं लेकिन उपयोग के मामले पूरी तरह से वैध नहीं हैं IMHO। यदि कोई अमरूद और विशेष रूप से संग्रह से संबंधित सामानों की जांच करता है , तो इसे स्टैटिक विधियों के साथ किया गया है। उदा। Lists.newArrayList () जो आपको लिखने की अनुमति देता है

List<String> names = Lists.newArrayList();

या स्थिर आयात के साथ

import static com.google.common.collect.Lists.*;
...
List<String> names = newArrayList();
List<String> names = newArrayList("one", "two", "three");

अमरूद में इस तरह की अन्य बहुत शक्तिशाली विशेषताएं हैं और मैं वास्तव में <> के लिए अधिक उपयोगों के बारे में नहीं सोच सकता।

यह अधिक उपयोगी होता यदि वे डायमंड ऑपरेटर के व्यवहार को डिफ़ॉल्ट बनाने के लिए जाते, अर्थात, टाइप को अभिव्यक्ति के बाईं ओर से या यदि बाईं ओर के प्रकार को दाईं ओर से बांटा गया था। उत्तरार्द्ध स्काला में होता है।


3

सामान्य प्रकार की घोषणा करते समय हीरे के ऑपरेटर के लिए बिंदु केवल कोड की टाइपिंग को कम करना है। रनटाइम पर इसका कोई प्रभाव नहीं पड़ता है।

एकमात्र अंतर यदि आप जावा 5 और 6 में निर्दिष्ट करते हैं,

List<String> list = new ArrayList();

यह है कि आपको निर्दिष्ट @SuppressWarnings("unchecked")करना होगा list(अन्यथा आपको एक अनियंत्रित डाली चेतावनी मिलेगी)। मेरी समझ यह है कि हीरा ऑपरेटर विकास को आसान बनाने की कोशिश कर रहा है। यह कुछ भी नहीं करने के लिए कुछ भी नहीं है जेनरिक के रनिंग निष्पादन पर।


आपको उस एनोटेशन का उपयोग करने की भी आवश्यकता नहीं है। कम से कम ग्रहण में, आप संकलक को बता सकते हैं कि आपको इस बारे में चेतावनी नहीं दी गई है और आप ठीक हैं ...
ज़ेरुस

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