[Java] उपप्रकारों की सूची में आप सुपरटेप्स की सूची कैसे डालते हैं?


Answers

जेनेरिकों का कास्टिंग संभव नहीं है, लेकिन यदि आप किसी अन्य तरीके से सूची को परिभाषित करते हैं तो इसमें टेस्टबी स्टोर करना संभव है:

List<? extends TestA> myList = new ArrayList<TestA>();

जब भी आप सूची में ऑब्जेक्ट्स का उपयोग कर रहे हों तब भी आपके पास टाइप करने की जांच होगी।

Question

उदाहरण के लिए, मान लें कि आपके पास दो कक्षाएं हैं:

public class TestA {}
public class TestB extends TestA{}

मेरे पास एक तरीका है जो एक List<TestA> और मैं उस सूची में सभी ऑब्जेक्ट्स को List<TestA> डालना चाहता हूं ताकि मैं एक List<TestA> साथ समाप्त हो List<TestB>




जावा 8 के साथ, आप वास्तव में कर सकते हैं

List<TestB> variable = collectionOfListA
    .stream()
    .map(e -> (TestB) e)
    .collect(Collectors.toList());



यदि आपके पास कक्षा TestA का ऑब्जेक्ट है, तो आप इसे TestA नहीं डाल सकते हैं। प्रत्येक TestA एक TestA , लेकिन दूसरी तरफ नहीं।

निम्नलिखित कोड में:

TestA a = new TestA();
TestB b = (TestB) a;

दूसरी पंक्ति ClassCastException फेंक देगी।

यदि आप ऑब्जेक्ट स्वयं TestA तो आप केवल TestA संदर्भ ही डाल सकते हैं। उदाहरण के लिए:

TestA a = new TestB();
TestB b = (TestB) a;

इसलिए, आप हमेशा TestA सूची में TestA की एक सूची नहीं डाल सकते हैं।




मुझे पता है कि एकमात्र तरीका कॉपी करके है:

List<TestB> list = new ArrayList<TestB> (
    Arrays.asList (
        testAList.toArray(new TestB[0])
    )
);



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

कारण यह सामान्य रूप से काम नहीं करता है, पहले से ही अन्य उत्तरों में बताया गया है: रूपांतरण वास्तव में मान्य है या नहीं, मूल सूची में मौजूद वस्तुओं के प्रकारों पर निर्भर करता है। जब सूची में ऑब्जेक्ट्स होते हैं जिनका प्रकार TestA टाइप नहीं होता है, लेकिन TestA के एक अलग उप-वर्ग के TestA , तो कास्ट मान्य नहीं है।

बेशक, कास्ट मान्य हो सकता है। आपके पास कभी-कभी उन प्रकारों के बारे में जानकारी होती है जो संकलक के लिए उपलब्ध नहीं हैं। इन मामलों में, सूचियों को कास्ट करना संभव है, लेकिन सामान्य रूप से, इसकी अनुशंसा नहीं की जाती है :

कोई भी हो सकता है ...

  • ... पूरी सूची डालें या
  • ... सूची के सभी तत्वों को कास्ट करें

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

इन नकली Collections#checkedCollection को डीबग करने की समस्या को विधियों के Collections#checkedCollection परिवार के साथ कम किया जा सकता है।

प्रकार के आधार पर सूची फ़िल्टरिंग

किसी List<Supertype> से किसी List<Subtype> में कनवर्ट करने का एक और प्रकार-सुरक्षित तरीका वास्तव में सूची को फ़िल्टर करना है, और एक नई सूची बनाना जिसमें केवल कुछ तत्व हैं। ऐसी विधि के कार्यान्वयन के लिए स्वतंत्रता की कुछ डिग्री हैं (उदाहरण के लिए null प्रविष्टियों के उपचार के संबंध में), लेकिन एक संभावित कार्यान्वयन इस तरह दिख सकता है:

/**
 * Filter the given list, and create a new list that only contains
 * the elements that are (subtypes) of the class c
 * 
 * @param listA The input list
 * @param c The class to filter for
 * @return The filtered list
 */
private static <T> List<T> filter(List<?> listA, Class<T> c)
{
    List<T> listB = new ArrayList<T>();
    for (Object a : listA)
    {
        if (c.isInstance(a))
        {
            listB.add(c.cast(a));
        }
    }
    return listB;
}

इस विधि का उपयोग मनमानी सूचियों को फ़िल्टर करने के लिए किया जा सकता है (न केवल टाइप पैरामीटर के संबंध में दिए गए सबटाइप-सुपरटेप रिश्ते के साथ), जैसा कि इस उदाहरण में है:

// A list of type "List<Number>" that actually 
// contains Integer, Double and Float values
List<Number> mixedNumbers = 
    new ArrayList<Number>(Arrays.asList(12, 3.4, 5.6f, 78));

// Filter the list, and create a list that contains
// only the Integer values:
List<Integer> integers = filter(mixedNumbers, Integer.class);

System.out.println(integers); // Prints [12, 78]



आप एक्लिप्स संग्रह में selectInstances विधि का उपयोग कर सकते हैं। इसमें एक नया संग्रह बनाने में शामिल होगा, हालांकि यह स्वीकार किए गए समाधान के रूप में उतना कुशल नहीं होगा जो कास्टिंग का उपयोग करता है।

List<CharSequence> parent =
        Arrays.asList("1","2","3", new StringBuffer("4"));
List<String> strings =
        Lists.adapt(parent).selectInstancesOf(String.class);
Assert.assertEquals(Arrays.asList("1","2","3"), strings);

उदाहरण के लिए selectInstances को उदाहरण में दिखाने के लिए शामिल किया है कि selectInstances न केवल प्रकार को डाउनकास्ट करता है, बल्कि संग्रह में मिश्रित प्रकारों को फ़िल्टर भी किया जाएगा।

नोट: मैं ग्रहण संग्रह के लिए एक कमिटर हूं।




यह काम करना चाहिए

List<TestA> testAList = new ArrayList<>();
List<TestB> testBList = new ArrayList<>()
testAList.addAll(new ArrayList<>(testBList));



स्टीव कुओ के उल्लेख के रूप में आप List<TestA> List<TestB> नहीं डाल सकते हैं लेकिन आप List<TestA> की सामग्री को डंप कर सकते हैं। निम्नलिखित आज़माएं:

List<TestA> result = new List<TestA>();
List<TestB> data = new List<TestB>();
result.addAll(data);

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