सूची <SubClass> को सूची <BaseClass> में डालने का सबसे कारगर तरीका है


134

मैं एक है List<SubClass>कि मैं एक के रूप में इलाज करना चाहते हैं List<BaseClass>। ऐसा लगता है जैसे कि यह कास्टिंग एक के बाद एक समस्या नहीं होनी चाहिए SubClassएक करने के लिए BaseClassएक तस्वीर है, लेकिन मेरे संकलक शिकायत है कि कलाकारों के असंभव है।

तो, एक ही वस्तु के रूप में एक संदर्भ पाने के लिए सबसे अच्छा तरीका क्या है List<BaseClass>?

अभी मैं एक नई सूची बना रहा हूं और पुरानी सूची की प्रतिलिपि बना रहा हूं:

List<BaseClass> convertedList = new ArrayList<BaseClass>(listOfSubClass)

लेकिन जैसा कि मैं इसे समझता हूं कि एक पूरी तरह से नई सूची बनानी होगी। यदि संभव हो तो मैं मूल सूची का संदर्भ चाहूंगा!


3
उत्तर आपके पास यहां है: stackoverflow.com/questions/662508/…
lukastymo

जवाबों:


175

इस तरह के असाइनमेंट के लिए सिंटैक्स एक वाइल्डकार्ड का उपयोग करता है:

List<SubClass> subs = ...;
List<? extends BaseClass> bases = subs;

यह महसूस करना महत्वपूर्ण है कि अ List<SubClass>का विनिमेय नहीं है List<BaseClass>। कोड जो List<SubClass>सूची के प्रत्येक आइटम की अपेक्षा करेगा एक संदर्भ रखता है a SubClass। यदि कोड के एक अन्य भाग को सूची के रूप में संदर्भित किया जाता है List<BaseClass>, तो संकलक शिकायत करेगा जब एक BaseClassया AnotherSubClassडाला जाता है। लेकिन यह ClassCastExceptionकोड के पहले टुकड़े के लिए कारण होगा , जो मानता है कि सूची में सब कुछ एक है SubClass

सामान्य संग्रह जावा में सरणियों के समान व्यवहार नहीं करते हैं। एरेव्स सहसंयोजक हैं; यह है, यह करने की अनुमति है:

SubClass[] subs = ...;
BaseClass[] bases = subs;

यह अनुमति है, क्योंकि इसके तत्वों का प्रकार "सरणी" जानता है। यदि कोई ऐसी चीज़ को संग्रहीत करने का प्रयास करता है SubClassजो सरणी में नहीं है ( basesसंदर्भ के माध्यम से ), तो रनटाइम अपवाद फेंक दिया जाएगा।

सामान्य संग्रह उनके घटक प्रकार को " नहीं " जानते हैं; यह जानकारी संकलन समय पर "मिटा" है। इसलिए, जब कोई अमान्य स्टोर होता है, तो वे रनटाइम अपवाद नहीं उठा सकते हैं। इसके बजाय, ClassCastExceptionसंग्रह से मूल्य को पढ़ने पर कोड में कुछ दूर, कठिन-से-सहयोगी बिंदु पर एक उठाया जाएगा। यदि आप टाइप सुरक्षा के बारे में चेतावनी चेतावनी देते हैं, तो आप रनटाइम में इन प्रकार की त्रुटियों से बचेंगे।


ध्यान रखें कि Arrays के साथ, ClassCastException के बजाय, जब Sub Subass (या व्युत्पन्न) प्रकार का नहीं है, तो किसी ऑब्जेक्ट को प्राप्त करते समय, आपको सम्मिलित करते समय एक ArrayStoreException मिलेगी।
एक्सल

विशेष रूप से इस स्पष्टीकरण के लिए धन्यवाद कि दो सूचियों को समान क्यों नहीं माना जा सकता है।
रिले लर्क

जावा प्रकार प्रणाली दिखावा करती है कि सरणियाँ सहसंयोजक हैं, लेकिन वे वास्तव में प्रतिस्थापन योग्य नहीं हैं, जो ArrayStoreException द्वारा सिद्ध किया गया है।
पाओलो एबरमन

38

एरिकसन ने पहले ही समझाया कि आप ऐसा क्यों नहीं कर सकते, लेकिन यहां कुछ समाधान हैं:

यदि आप केवल अपनी आधार सूची से तत्वों को निकालना चाहते हैं, तो सिद्धांत रूप में आपकी प्राप्त करने की विधि को लेने के रूप में घोषित किया जाना चाहिए List<? extends BaseClass>

लेकिन अगर यह नहीं है और आप इसे बदल नहीं सकते हैं, तो आप सूची को उसके साथ लपेट सकते हैं Collections.unmodifiableList(...), जो तर्क के पैरामीटर के एक सुपरपेप की सूची को वापस करने की अनुमति देता है। (यह सम्मिलन की कोशिशों पर UnsupportedOperationException को फेंकने से प्रकार की समस्या से बचा जाता है।)


महान! उस एक को नहीं जानता था।
केयूएलजे

आपका बहुत बहुत धन्यवाद!
वूलैंड

14

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

List<BaseClass> baseList = (List)new ArrayList<SubClass>();

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


3

नीचे एक उपयोगी स्निपेट है जो काम करता है। यह एक नई सरणी सूची का निर्माण करता है लेकिन जेवीएम ऑब्जेक्ट का निर्माण सिर के ऊपर महत्वपूर्ण है।

मैंने देखा कि अन्य उत्तर गैर-जरूरी हैं।

List<BaseClass> baselist = new ArrayList<>(sublist);

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

ओवरहेड निरर्थक नहीं है, क्योंकि यह भी (उथले) हर संदर्भ की प्रतिलिपि है। जैसे कि यह सूची के आकार के साथ बढ़ता है, इसलिए यह एक ओ (एन) ऑपरेशन है।
john16384

2

मैंने उस उत्तर को याद किया जहां आपने मूल सूची में एक डबल कास्ट का उपयोग किया था। तो यहाँ यह पूर्णता के लिए है:

List<BaseClass> baseList = (List<BaseClass>)(List<?>)subList;

कुछ भी कॉपी नहीं किया गया है, और ऑपरेशन तेज है। हालाँकि, आप संकलक को यहाँ धोखा दे रहे हैं, इसलिए आपको सूची को इस तरह से संशोधित न करने के लिए पूरी तरह से सुनिश्चित करना चाहिए कि subListएक अलग उप प्रकार की वस्तुओं से शुरू होता है। अपरिवर्तनीय सूचियों के साथ काम करते समय यह आमतौर पर एक मुद्दा नहीं होता है।


1

List<BaseClass> convertedList = Collections.checkedList(listOfSubClass, BaseClass.class)


5
यह काम नहीं करना चाहिए, क्योंकि इस पद्धति के लिए सभी तर्क और परिणाम समान प्रकार के पैरामीटर लेते हैं।
पाओलो एबरमन

आप सही कह रहे हैं। किसी कारण के लिए, मैं इस धारणा के तहत था कि यह विधि एक सामान्य संग्रह को बनाए रखने के लिए उपयोगी थी। अभी भी इस तरह से इस्तेमाल किया जा सकता है और यह एक "सुरक्षित" संग्रह प्रदान करेगा यदि आप सप्रेसवरिंग का उपयोग करना चाहते हैं।
jtahlborn

1

आप जो करने की कोशिश कर रहे हैं वह बहुत उपयोगी है और मुझे लगता है कि मुझे इसे कोड में बहुत बार करने की आवश्यकता है जो मैं लिखता हूं। एक उदाहरण उपयोग मामला:

कहें कि हमारे पास एक इंटरफ़ेस है Fooऔर हमारे पास एक zorkingपैकेज है जिसमें एक पैकेज ZorkingFooManagerहै और पैकेज-प्राइवेट के उदाहरणों का प्रबंधन करता है ZorkingFoo implements Foo। (एक बहुत ही सामान्य परिदृश्य।)

तो, ZorkingFooManagerएक को शामिल करने की जरूरत है private Collection<ZorkingFoo> zorkingFoosलेकिन यह एक को बेनकाब करने की जरूरत है public Collection<Foo> getAllFoos()

अधिकांश जावा प्रोग्रामर getAllFoos()एक नए आवंटन के रूप में लागू करने से पहले दो बार नहीं सोचेंगे ArrayList<Foo>, इसे सभी तत्वों के साथ आबादी देंगे zorkingFoosऔर इसे वापस करेंगे। मुझे लगा कि मनोरंजन का आनंद ले रहे हैं, पूरे ग्रह पर लाखों मशीनों पर चलने वाले जावा कोड द्वारा उपभोग किए जाने वाले सभी घड़ी चक्रों का लगभग 30% कुछ भी नहीं कर रहा है, लेकिन ArrayLists की ऐसी बेकार प्रतियां बना रहा है जो उनके निर्माण के बाद कचरा एकत्र किए गए माइक्रोसेकंड हैं।

इस समस्या का समाधान, निश्चित रूप से, संग्रह को डाउन-कास्टिंग करना है। यहाँ यह करने का सबसे अच्छा तरीका है:

static <T,U extends T> List<T> downCastList( List<U> list )
{
    return castList( list );
}

जो हमें castList()फंक्शन में लाता है :

static <T,E> List<T> castList( List<E> list )
{
    @SuppressWarnings( "unchecked" )
    List<T> result = (List<T>)list;
    return result;
}

resultजावा भाषा के विकृत होने के कारण मध्यवर्ती चर आवश्यक है:

  • return (List<T>)list;एक "अनियंत्रित कास्ट" अपवाद पैदा करता है; अब तक तो सब ठीक है; परन्तु फिर:

  • @SuppressWarnings( "unchecked" ) return (List<T>)list; दमन-चेतावनी एनोटेशन का अवैध उपयोग है।

इसलिए, भले ही यह @SuppressWarningsएक returnबयान पर उपयोग करने के लिए कोषेर नहीं है , यह एक असाइनमेंट पर इसका उपयोग करने के लिए स्पष्ट रूप से ठीक है, इसलिए अतिरिक्त "परिणाम" चर इस समस्या को हल करता है। (इसे कंपाइलर द्वारा या जेआईटी द्वारा वैसे भी अनुकूलित किया जाना चाहिए।)


0

कुछ इस तरह से भी काम करना चाहिए:

public static <T> List<T> convertListWithExtendableClasses(
    final List< ? extends T> originalList,
    final Class<T> clazz )
{
    final List<T> newList = new ArrayList<>();
    for ( final T item : originalList )
    {
        newList.add( item );
    }// for
    return newList;
}

वास्तव में नहीं पता कि ग्रहण में क्लैज़ की आवश्यकता क्यों है ..


0

कैसे सभी तत्वों को कास्टिंग के बारे में। यह एक नई सूची बनाएगा, लेकिन पुरानी सूची से मूल वस्तुओं को संदर्भित करेगा।

List<BaseClass> convertedList = listOfSubClass.map(x -> (BaseClass)x).collect(Collectors.toList());

यह कोड का पूरा टुकड़ा है जो सुपर क्लास को सब क्लास की सूची देने का काम करता है।
कनपार्थिकिरन

0

यह सब क्लास लिस्ट को सुपर क्लास में डालने के लिए, जेनरिक का उपयोग करके कोड का पूरा काम करने वाला टुकड़ा है।

कॉलर विधि जो उपवर्ग प्रकार से गुजरती है

List<SubClass> subClassParam = new ArrayList<>();    
getElementDefinitionStatuses(subClassParam);

आधार विधि के किसी भी उपप्रकार को स्वीकार करने वाली Callee विधि

private static List<String> getElementDefinitionStatuses(List<? extends 
    BaseClass> baseClassVariableName) {
     return allElementStatuses;
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.