java - जावा संग्रह क्यों सामान्य तरीकों को हटा नहीं रहे हैं?




api generics collections (9)

Collection.remove (ऑब्जेक्ट ओ) जेनेरिक क्यों नहीं है?

लगता है कि Collection<E> boolean remove(E o); हो सकता है boolean remove(E o);

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


Answers

जोश ब्लोच और बिल पुग जावा पज़लर्स IV में इस मुद्दे का उल्लेख करते हैं : द फैंटम रेफरेंस मेनस, क्लोन का हमला, और शिफ्ट का बदला

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

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


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

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

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


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


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

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

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


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

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


अन्य उत्तरों के अलावा, एक और कारण है कि विधि को 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 विधि को परिभाषित करने के लिए ज़िम्मेदार है। बिल्डिंग भविष्यवाणी इस तरह से बहुत आसान हो जाती है।


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

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

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


एक बिंदु मुझे लगता है कि other answers उल्लेख करने के लिए जोड़ा जाना चाहिए कि जबकि

List<Dog> जावा में एक List<Animal>

यह भी सच है कि

कुत्तों की एक सूची है- अंग्रेजी में जानवरों की एक सूची (अच्छी तरह से, उचित व्याख्या के तहत)

जिस तरह ओपी का अंतर्ज्ञान काम करता है - जो पूरी तरह से पाठ्यक्रम के लिए मान्य है - बाद की सजा है। हालांकि, अगर हम इस अंतर्ज्ञान को लागू करते हैं तो हमें ऐसी भाषा मिलती है जो जावा-एस्क्यू अपने प्रकार की प्रणाली में नहीं है: मान लीजिए कि हमारी भाषा कुत्तों की सूची में एक बिल्ली जोड़ने की अनुमति देती है। इसका क्या मतलब होगा? इसका मतलब यह होगा कि सूची कुत्तों की एक सूची बन जाती है, और केवल जानवरों की एक सूची बनी हुई है। और स्तनधारियों की एक सूची, और चौकोर की एक सूची।

इसे एक और तरीका रखने के लिए: जावा में एक List<Dog> का अर्थ अंग्रेजी में "कुत्तों की एक सूची" नहीं है, इसका अर्थ है "एक सूची जिसमें कुत्ते हो सकते हैं, और कुछ और नहीं"।

अधिक आम तौर पर, ओपी की अंतर्ज्ञान खुद को ऐसी भाषा की तरफ उधार देती है जिसमें ऑब्जेक्ट्स पर ऑपरेशन उनके प्रकार को बदल सकते हैं , या बल्कि, ऑब्जेक्ट का प्रकार इसके मान का एक (गतिशील) फ़ंक्शन है।





java api generics collections