जावा कलेक्शंस जेनेरिक तरीकों को क्यों नहीं हटाते?


144

क्यों नहीं है संग्रह। सुप्रीम (ऑब्जेक्ट ओ) सामान्य?

जैसे लग Collection<E>सकता थाboolean remove(E o);

फिर, जब आप गलती से (उदाहरण के लिए) Set<String>प्रत्येक व्यक्ति के बजाय स्ट्रिंग से निकालने की कोशिश करते हैं Collection<String>, तो यह बाद में डिबगिंग समस्या के बजाय एक संकलन समय त्रुटि होगी।


13
जब आप इसे ऑटोबॉक्सिंग के साथ जोड़ते हैं तो यह वास्तव में आपको काट सकता है। यदि आप किसी सूची से कुछ निकालने का प्रयास करते हैं और आप इसे हटाने के लिए एक इंटेगर पास करते हैं तो यह निष्कासन (ऑब्जेक्ट) विधि कहलाता है।
ScArcher2

2
मानचित्र के बारे में इसी तरह का प्रश्न: stackoverflow.com/questions/857420/…
AlikElzin-kilaka

जवाबों:


73

जोश बलोच और बिल पुघ ने जावा पूज़लर्स IV में इस मुद्दे का उल्लेख किया : द फैंटम रेफरेंस मेनस, अटैक ऑफ़ द क्लोन, और रिवेंज ऑफ़ द शिफ़्ट

जोश बलोच (6:41) कहते हैं कि उन्होंने मैप की विधि प्राप्त करने, विधि और कुछ अन्य को हटाने का प्रयास किया, लेकिन "यह बस काम नहीं किया"।

बहुत सारे वाजिब प्रोग्राम हैं जिन्हें जेनरेट नहीं किया जा सकता है यदि आप केवल पैरामीटर प्रकार के रूप में सामान्य प्रकार के संग्रह की अनुमति देते हैं। उदाहरण उसके द्वारा दिए गए एक के एक चौराहे है Listकी Numberहै और एक Listकी Longरों।


6
क्यों जोड़ें () टाइप किया गया पैरामीटर ले सकता है, लेकिन हटा () अभी भी मेरी समझ से परे नहीं है। जोश बलोच संग्रह प्रश्नों के लिए निश्चित संदर्भ होगा। यह सब हो सकता है कि मैं एक समान संग्रह कार्यान्वयन बनाने और खुद को देखने की कोशिश किए बिना मिलूं। :( धन्यवाद।
क्रिस माज़ोला

2
क्रिस - जावा जेनेरिक ट्यूटोरियल पीडीएफ पढ़ें, यह बताएगा कि क्यों।
JeeBee

42
वास्तव में, यह बहुत आसान है! यदि जोड़ () ने एक गलत ऑब्जेक्ट लिया, तो यह संग्रह को तोड़ देगा। इसमें ऐसी चीजें होंगी जो इसे नहीं माना जाता है! यह हटाने (), या शामिल () के लिए मामला नहीं है।
केविन बोर्रिलियन

12
संयोग से, वह मूल नियम - केवल संग्रह में वास्तविक क्षति को रोकने के लिए प्रकार के मापदंडों का उपयोग करना - पूरी लाइब्रेरी में लगातार अनुसरण किया जाता है।
केविन बोर्रिलियन

3
@ केविनबौरिलियन: मैं वर्षों से (जावा और सी # दोनों में) जेनरिक के साथ काम कर रहा हूं, बिना यह महसूस किए कि "वास्तविक क्षति" नियम भी मौजूद है ... लेकिन अब जब मैंने देखा है कि सीधे तौर पर यह 100% सही अर्थ बताता है। मछली के लिए धन्यवाद !!! सिवाय अब मैं वापस जाने के लिए और अपने कार्यान्वयन को देखने के लिए मजबूर महसूस करता हूं, यह देखने के लिए कि क्या कुछ तरीके हो सकते हैं और इसलिए उन्हें पतित होना चाहिए। आह।
corlettk

74

remove()(में और Mapसाथ ही Collection) सामान्य नहीं है क्योंकि आपको किसी भी प्रकार की वस्तु में पास करने में सक्षम होना चाहिए remove()। हटाए गए ऑब्जेक्ट का उसी प्रकार का होना आवश्यक नहीं है जैसा कि आप जिस वस्तु से गुजरते हैं remove(); इसके लिए केवल यह आवश्यक है कि वे समान हों। के विनिर्देशन से remove(), इस तरह remove(o)की वस्तु को हटाता है । ध्यान दें कि आवश्यकता नहीं है और एक ही प्रकार का होना चाहिए। यह इस तथ्य से आता है कि विधि एक पैरामीटर के रूप में होती है, न कि ऑब्जेक्ट के समान प्रकार।e(o==null ? e==null : o.equals(e))trueoeequals()Object

हालांकि, यह आमतौर पर सही हो सकता है कि कई वर्गों ने equals()परिभाषित किया है ताकि इसकी वस्तुएं केवल अपने वर्ग की वस्तुओं के बराबर हो सकें, यह निश्चित रूप से हमेशा ऐसा नहीं होता है। उदाहरण के लिए, विनिर्देशन List.equals()कहता है कि दो सूची वस्तुएँ समान हैं यदि वे दोनों सूचियाँ हैं और समान सामग्री हैं, भले ही वे अलग-अलग कार्यान्वयन हों List। इसलिए इस प्रश्न में उदाहरण के लिए वापस आना, Map<ArrayList, Something>मेरे लिए remove()एक LinkedListतर्क के रूप में कॉल करना संभव है , और यह उस कुंजी को हटा देना चाहिए जो समान सामग्रियों के साथ एक सूची है। यह संभव नहीं होगा यदि remove()सामान्य और इसके तर्क प्रकार को प्रतिबंधित किया जाए।


1
लेकिन अगर आप मानचित्र को मानचित्र <सूची, कुछ> (एरेलेलिस्ट के बजाय) के रूप में परिभाषित करते हैं, तो लिंकडलिस्ट का उपयोग करना संभव होगा। मुझे लगता है कि यह उत्तर गलत है।
बजे AlikElzin-kilaka

3
उत्तर सही प्रतीत होता है, लेकिन अधूरा। यह केवल यह पूछने के लिए उधार देता है कि बिल्ली ने equals()विधि को सामान्य रूप से क्यों नहीं किया ? मैं इस "उदारवादी" दृष्टिकोण के बजाय सुरक्षा टाइप करने के लिए अधिक लाभ देख सकता था। मुझे लगता है कि वर्तमान कार्यान्वयन वाले अधिकांश मामले इस लचीलेपन के बारे में नहीं बल्कि remove()विधि द्वारा लाए जाने वाले आनंद के बारे में हैं।
kellogs

2
@kloglogs: " equals()विधि को सामान्य बनाने" से आपका क्या अर्थ होगा ?
newacct

5
@MattBall: "जहां T घोषित वर्ग है" लेकिन जावा में ऐसा कोई सिंटैक्स नहीं है। Tवर्ग पर एक प्रकार के पैरामीटर के रूप में घोषित किया जाना चाहिए, और Objectकिसी भी प्रकार के पैरामीटर नहीं हैं। टाइप करने का कोई तरीका नहीं है जो "घोषित वर्ग" को संदर्भित करता है।
newacct

3
मुझे लगता है कि केलॉग्स कह रहे हैं कि क्या समानता के Equality<T>साथ एक सामान्य इंटरफ़ेस था equals(T other)। तब आप कर सकते हैं remove(Equality<T> o)और oबस कुछ वस्तु है कि दूसरे की तुलना की जा सकती है T
वेस्टन

11

क्योंकि यदि आपका प्रकार पैरामीटर वाइल्डकार्ड है, तो आप जेनेरिक हटाने की विधि का उपयोग नहीं कर सकते।

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

मैंने शायद इसे बहुत अच्छी तरह से समझाया नहीं था, लेकिन यह मेरे लिए काफी तर्कसंगत लगता है।


1
क्या आप इस पर थोड़ा विस्तार कर सकते हैं?
थॉमस ओवेन्स

6

अन्य उत्तरों के अलावा, एक और कारण है कि विधि को एक को स्वीकार करना चाहिए Object, जो कि विधेय है। निम्नलिखित नमूने पर विचार करें:

class Person {
    public String name;
    // override equals()
}
class Employee extends Person {
    public String company;
    // override equals()
}
class Developer extends Employee {
    public int yearsOfExperience;
    // override equals()
}

class Test {
    public static void main(String[] args) {
        Collection<? extends Person> people = new ArrayList<Employee>();
        // ...

        // to remove the first employee with a specific name:
        people.remove(new Person(someName1));

        // to remove the first developer that matches some criteria:
        people.remove(new Developer(someName2, someCompany, 10));

        // to remove the first employee who is either
        // a developer or an employee of someCompany:
        people.remove(new Object() {
            public boolean equals(Object employee) {
                return employee instanceof Developer
                    || ((Employee) employee).company.equals(someCompany);
        }});
    }
}

मुद्दा यह है कि removeविधि को पारित किया जा रहा है विधि को परिभाषित करने के लिए जिम्मेदार है equals। बिल्डिंग की भविष्यवाणी इस तरह से बहुत सरल हो जाती है।


निवेशक? (फिलर फिलर फिलर)
मैट आर

3
इस सूची को इस प्रकार कार्यान्वित किया गया है yourObject.equals(developer), जैसा कि संग्रह API में प्रलेखित है: java.sun.com/javase/6/docs/api/java/util/…
एली

13
यह मेरे लिए एक गाली जैसा लगता है
RAY

7
यह दुरुपयोग है क्योंकि आपकी विधेय वस्तु equalsविधि के अनुबंध को तोड़ती है , अर्थात समरूपता। हटाने की विधि केवल इसके विनिर्देशन के लिए बाध्य है जब तक कि आपकी वस्तुएं बराबर / हैशकोड कल्पना को पूरा कर रही हैं, इसलिए किसी भी कार्यान्वयन को अन्य तरीके से तुलना करने के लिए स्वतंत्र होगा। इसके अलावा, आपकी विधेय वस्तु .hashCode()विधि को लागू नहीं करती है (बराबर के लिए लगातार लागू नहीं कर सकती है), इस प्रकार हटाएं कॉल कभी भी हैश-आधारित संग्रह (जैसे हैशसेट या हैशपॉट.कीक्स) () पर काम नहीं करेगा। यह ArrayList के साथ काम करता है शुद्ध भाग्य है।
पाओलो एबरमन

3
(मैं जेनेरिक प्रकार के प्रश्न पर चर्चा नहीं कर रहा हूं - यह पहले से ही अनुमानित था - केवल, यहाँ विधेय के लिए बराबरी का उपयोग।) बेशक हाशप और हैशेट हैश कोड के लिए जाँच कर रहे हैं, और ट्रीसेट / मैप तत्वों के क्रम का उपयोग कर रहे हैं। । फिर भी, वे पूरी तरह से लागू करते हैं Collection.remove, इसके अनुबंध को तोड़ने के बिना (यदि ऑर्डर बराबर के बराबर है)। और एक विविध ArrayList (या AbstractCollection, मुझे लगता है) बराबर कॉल के साथ घूमता है फिर भी अनुबंध को सही ढंग से लागू करेगा - यदि आप equalsअनुबंध को तोड़ रहे हैं तो यह आपकी गलती नहीं है ।
पाओलो एबरमैन

5

मान लें एक का संग्रह है Cat, और प्रकार के कुछ वस्तु संदर्भ Animal, Cat, SiameseCat, और Dog। संग्रह से यह पूछना कि क्या इसके द्वारा संदर्भित वस्तु Catया SiameseCatसंदर्भ उचित है। यह पूछे जाने पर कि क्या इसमें Animalसंदर्भ द्वारा संदर्भित वस्तु शामिल है , धूमिल लग सकती है, लेकिन यह अभी भी पूरी तरह से उचित है। प्रश्न में वस्तु, सब के बाद, एक हो सकता है Cat, और संग्रह में दिखाई दे सकता है।

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


1
"यदि पारित-वस्तु ऑब्जेक्ट संदर्भ एक असंबंधित प्रकार का है, तो कोई रास्ता नहीं है कि संग्रह संभवतः इसे शामिल कर सकता है" गलत। इसमें केवल इसके बराबर कुछ होना चाहिए; और विभिन्न वर्गों की वस्तुएँ समान हो सकती हैं।
नवसंवत्सर

"लग सकता है, लेकिन यह अभी भी पूरी तरह से उचित है" क्या यह है? एक ऐसी दुनिया पर विचार करें, जहाँ एक प्रकार की वस्तु को हमेशा दूसरे प्रकार की वस्तु के साथ समानता के लिए जाँच नहीं की जा सकती है, क्योंकि यह किस प्रकार के बराबर हो सकती है, इसे मानकीकृत किया जाता है ( Comparableआपके द्वारा तुलना किए जा सकने वाले प्रकारों के लिए इसे कैसे मानकीकृत किया जाता है)। तब लोगों को किसी असंबंधित प्रकार के कुछ पारित करने की अनुमति देना उचित नहीं होगा।
नवसंवत्सर

@newacct: दिए गए वस्तुओं: परिमाण तुलना और समानता तुलना में एक बुनियादी फर्क नहीं है Aऔर Bएक प्रकार की है, और Xऔर Yइस तरह है कि एक और की, A> B, और X> Y। या तो A> Yऔर Y< A, या X> Bऔर B< X। वे संबंध केवल तभी मौजूद हो सकते हैं यदि परिमाण की तुलना दोनों प्रकारों के बारे में जानते हैं। इसके विपरीत, किसी वस्तु की समानता तुलना विधि किसी अन्य प्रकार की किसी भी चीज के लिए किसी भी प्रकार की असमानता की घोषणा कर सकती है, बिना किसी प्रश्न के अन्य प्रकार के बारे में कुछ भी जाने बिना। एक प्रकार की वस्तु का Catकोई अंदाजा नहीं हो सकता है कि क्या है ...
सुपरकैट

... "से अधिक" या "एक प्रकार की वस्तु से कम" FordMustang, लेकिन यह कहने में कोई कठिनाई नहीं होनी चाहिए कि क्या यह ऐसी वस्तु के बराबर है (उत्तर, स्पष्ट रूप से, "नहीं")।
सुपरकैट

4

मुझे हमेशा लगा कि यह हटा दिया गया है (क्योंकि आपके पास यह बताने का कोई कारण नहीं है कि आप किस प्रकार की वस्तु देते हैं। यह काफी आसान है, भले ही, यह जांचने के लिए कि क्या वह वस्तु संग्रह में से एक है, क्योंकि यह किसी भी चीज़ पर बराबर () कॉल कर सकता है। यह सुनिश्चित करने के लिए कि यह केवल उस प्रकार की वस्तुओं में है, यह सुनिश्चित करने के लिए ऐड पर प्रकार की जांच करें


0

यह एक समझौता था। दोनों दृष्टिकोणों से उनका लाभ है:

  • remove(Object o)
    • अधिक लचीला है। उदाहरण के लिए यह संख्याओं की सूची के माध्यम से पुनरावृति करने और उन्हें लंबी सूची से निकालने की अनुमति देता है।
    • इस लचीलेपन का उपयोग करने वाला कोड अधिक आसानी से उत्पन्न किया जा सकता है
  • remove(E e) संकलित समय में सूक्ष्म बगों का पता लगाने के लिए सबसे अधिक कार्यक्रम क्या करना चाहते हैं, इसके लिए अधिक प्रकार की सुरक्षा लाता है, जैसे गलती से शॉर्ट्स की सूची से पूर्णांक को हटाने की कोशिश करना।

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


-1

निकालें एक सामान्य विधि नहीं है, ताकि गैर-जेनेरिक संग्रह का उपयोग करने वाला मौजूदा कोड अभी भी संकलित हो और अभी भी समान व्यवहार हो।

देखें http://www.ibm.com/developerworks/java/library/j-jtp01255.html जानकारी के लिए।

संपादित करें: एक टिप्पणीकार पूछता है कि जोड़ विधि सामान्य क्यों है। [... मेरी व्याख्या हटा दी ...] दूसरे टिप्पणीकार ने मेरे से फायरबर्ड84 के सवाल का जवाब दिया।


2
फिर ऐड विधि जेनेरिक क्यों है?
बॉब गेटी ने

@ firebird84 remove (ऑब्जेक्ट) गलत प्रकार की वस्तुओं को नजरअंदाज करता है, लेकिन हटाने (E) एक संकलन त्रुटि का कारण होगा। वह व्यवहार को बदल देगा।
नूह

: श्रग: - रनटाइम व्यवहार नहीं बदला गया है; संकलित त्रुटि रनटाइम व्यवहार नहीं है । ऐड मेथड का "व्यवहार" इस ​​प्रकार बदलता है।
जेसन एस

-2

एक और कारण इंटरफेस की वजह से है। इसे दिखाने के लिए एक उदाहरण यहां दिया गया है:

public interface A {}

public interface B {}

public class MyClass implements A, B {}

public static void main(String[] args) {
   Collection<A> collection = new ArrayList<>();
   MyClass item = new MyClass();
   collection.add(item);  // works fine
   B b = item; // valid
   collection.remove(b); /* It works because the remove method accepts an Object. If it was generic, this would not work */
}

आप कुछ ऐसा दिखा रहे हैं जिससे आप दूर हो सकते हैं क्योंकि remove()वह सहसंयोजक नहीं है। हालांकि, सवाल यह है कि क्या इसकी अनुमति दी जानी चाहिए। ArrayList#remove()मूल्य समानता के माध्यम से काम करता है, संदर्भ समानता नहीं। आप क्यों उम्मीद करेंगे कि एक के Bबराबर होगा A? आपके उदाहरण में, यह हो सकता है, लेकिन यह एक अजीब उम्मीद है। मैं आपको MyClassयहाँ एक तर्क देना चाहता हूँ ।
सेह

-3

क्योंकि यह मौजूदा (प्री-जावा 5) कोड को तोड़ देगा। जैसे,

Set stringSet = new HashSet();
// do some stuff...
Object o = "foobar";
stringSet.remove(o);

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


4
अगर मैं एक सूची <स्ट्रिंग> को तुरंत लिखता हूं, तो मैं केवल List.remove (someString) को कॉल करने में सक्षम होने की उम्मीद करूंगा; अगर मुझे पिछड़ी अनुकूलता का समर्थन करने की आवश्यकता है, तो मैं एक कच्ची सूची - सूची <?> का उपयोग करूंगा। तब मैं list.remove (someObject) को कॉल कर सकता हूं, नहीं?
क्रिस माजोला

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