उप-सूचियों की सूची में आप सुपरटेप की सूची कैसे डालते हैं?


244

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

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

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

जवाबों:


578

List<TestB>लगभग काम करने के लिए बस कास्टिंग ; लेकिन यह काम नहीं करता है क्योंकि आप एक सामान्य प्रकार के एक पैरामीटर को दूसरे में नहीं डाल सकते हैं। हालाँकि, आप एक मध्यवर्ती वाइल्डकार्ड प्रकार के माध्यम से कास्ट कर सकते हैं और इसे अनुमति दी जाएगी (जब से आप अनियंत्रित चेतावनी के साथ वाइल्डकार्ड प्रकारों में डाल सकते हैं और ले जा सकते हैं):

List<TestB> variable = (List<TestB>)(List<?>) collectionOfListA;

88
सभी उचित सम्मान के साथ, मुझे लगता है कि यह एक हैकी समाधान है - आप उस प्रकार को चकमा दे रहे हैं जिस प्रकार जावा आपको प्रदान करने का प्रयास कर रहा है। इस जावा ट्यूटोरियल को कम से कम देखें ( docs.oracle.com/javase/tutorial/java/generics/subtyping.html ) और विचार करें कि इसे इस तरह से ठीक करने से पहले आपको यह समस्या क्यों है।
jfritz42

63
@ jfritz42 मैं इसे "चकमा देना" प्रकार की सुरक्षा नहीं कहूंगा। इस मामले में आपको ज्ञान है कि जावा में नहीं है। यही कास्टिंग के लिए है।
प्लांकी

3
यह मुझे लिखने देता है: सूची <स्ट्रिंग> myList = (सूची <स्ट्रिंग>) (सूची <?>) (नया ArrayList <Integer> ()); यह रनटाइम पर क्रैश हो जाएगा। मैं सूची <जैसी कुछ पसंद करता हूं? फैली संख्या> myList = नया एरियरिस्ट <Integer> ();
बेंटाय

1
मैं ईमानदारी से @ jfritz42 से सहमत हूँ मैं एक ही मुद्दा रहा था और उस URL को देखा जो उसने प्रदान किया था और कास्टिंग के बिना मेरी समस्या को हल करने में सक्षम था।
सैम ओरोज्को

@ jfritz42 यह एक वस्तु को एक इंटीजर के लिए कास्टिंग से कार्यात्मक रूप से अलग नहीं है, जिसे संकलक द्वारा अनुमति दी गई है
kcazllerraf

116

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

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

जब आप सूची में वस्तुओं का उपयोग कर रहे हों तब भी आपके पास करने के लिए टाइपिंग की जाँच हो।


14
यह जावा सिंटैक्स भी मान्य नहीं है।
newacct 19

2
यह सच है, लेकिन यह तथ्यात्मक रूप से सही होने की तुलना में अधिक सचित्र था
Salandur

मेरे पास निम्न विधि है: createSingleString (सूची <; फैली हुई वस्तु> वस्तुएं) इस विधि के अंदर मैं एक स्ट्रिंग में सूची को गिराने के लिए String.valueOf (ऑब्जेक्ट) कहता हूं। इनपुट <लॉन्ग>, लिस्ट <बुलियन>, आदि के समय यह बहुत अच्छा काम करता है। धन्यवाद!
क्रिस स्प्रैग

2
क्या होगा अगर TestA इंटरफ़ेस है?
सुविटरुफ़ - आंद्रेई अपानसिक

8
क्या आप एक MCVE दे सकते हैं जहां यह वास्तव में काम करता है? मुझे मिलता है Cannot instantiate the type ArrayList<? extends TestA>
नटोमामी

42

आप वास्तव में नहीं कर सकते *:

उदाहरण इस जावा ट्यूटोरियल से लिया गया है

मान लें कि दो प्रकार Aऔर Bऐसे हैं B extends A। फिर निम्नलिखित कोड सही है:

    B b = new B();
    A a = b;

पिछला कोड मान्य है क्योंकि Bइसका उपवर्ग है A। अब, क्या होता है List<A>और List<B>?

यह पता चला है कि List<B>यह एक उपवर्ग नहीं है List<A>इसलिए हम लिख नहीं सकते

    List<B> b = new ArrayList<>();
    List<A> a = b; // error, List<B> is not of type List<A>

इसके अलावा, हम भी नहीं लिख सकते हैं

    List<B> b = new ArrayList<>();
    List<A> a = (List<A>)b; // error, List<B> is not of type List<A>

*: कास्टिंग को संभव बनाने के लिए हमें दोनों के लिए एक सामान्य माता-पिता की आवश्यकता है : List<A>और उदाहरण के लिए। निम्नलिखित है मान्य:List<B>List<?>

    List<B> b = new ArrayList<>();
    List<?> t = (List<B>)b;
    List<A> a = (List<A>)t;

हालाँकि, आपको एक चेतावनी मिलेगी। आप इसे @SuppressWarnings("unchecked")अपने तरीके से जोड़कर दबा सकते हैं ।


2
इस लिंक में सामग्री का उपयोग करने से अनियंत्रित रूपांतरण से बचा जा सकता है, क्योंकि कंपाइलर पूरे रूपांतरण को डिक्रिप्ट कर सकता है।
रयान एच

29

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

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

32
क्या यह वास्तव में सूची में है? क्योंकि यह पूरी सूची पर पुनरावृति करने के लिए संचालन की श्रृंखला की तरह अधिक पढ़ता है, प्रत्येक तत्व को कास्टिंग करता है, और फिर उन्हें वांछित प्रकार की एक नई त्वरित सूची में जोड़ देता है। एक और तरीका रखो, क्या आप Java7 के साथ ऐसा नहीं कर सकते हैं- एक new List<TestB>, एक लूप और एक कास्ट के साथ?
पैट्रिक एम।

5
ओपी से "और मैं उस सूची में सभी वस्तुओं को डालना चाहूंगा" - इसलिए वास्तव में, यह उन कुछ उत्तरों में से एक है जो इस प्रश्न का उत्तर देता है, भले ही यह सवाल यह नहीं है कि लोग यहां ब्राउज़ कर रहे हैं।
ल्यूक उशेरवुड

17

मुझे लगता है कि आप गलत दिशा में जा रहे हैं, हालांकि ... अगर विधि TestAवस्तुओं की सूची लौटाती है , तो यह वास्तव में उन्हें डालना सुरक्षित नहीं है TestB

मूल रूप से आप संकलक से पूछ रहे हैं कि आप TestBकिस प्रकार का संचालन करते हैं TestAजो उनका समर्थन नहीं करता है।


11

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


सामान्य रूप से काम नहीं करने का कारण पहले से ही अन्य उत्तरों में बताया गया है: रूपांतरण वास्तव में वैध है या नहीं, यह उन वस्तुओं पर निर्भर करता है जो मूल सूची में निहित हैं। जब सूची में ऐसी वस्तुएं होती हैं जिनका प्रकार प्रकार का नहीं होता है TestB, लेकिन एक अलग उपवर्ग का होता है TestA, तो कलाकार मान्य नहीं होता है।


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

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

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

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

तरीकों ClassCastExceptionsकी 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]

7

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

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

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


यह नीचे मतदान क्यों किया गया था? यह मेरे लिए उपयोगी था, और मुझे डाउन वोट के लिए कोई स्पष्टीकरण नहीं मिला।
राशि

7
निश्चित रूप से यह पोस्ट गलत नहीं है। लेकिन सवाल पूछने वाले को आखिरकार टेस्टबी की सूची चाहिए। उस स्थिति में यह उत्तर भ्रामक है। आप सूची <TestB> में सूची <TestA> सूची <TestA> .addAll (सूची <TestB>) द्वारा सभी तत्वों को जोड़ सकते हैं। लेकिन आप सूची <TestB> .addAll (सूची <TestA>) का उपयोग नहीं कर सकते हैं;
prageeth

6

सबसे अच्छा सुरक्षित तरीका कार्यान्वयन में एक AbstractListऔर डाली वस्तुओं को लागू करना है। मैंने ListUtilसहायक वर्ग बनाया :

public class ListUtil
{
    public static <TCastTo, TCastFrom extends TCastTo> List<TCastTo> convert(final List<TCastFrom> list)
    {
        return new AbstractList<TCastTo>() {
            @Override
            public TCastTo get(int i)
            {
                return list.get(i);
            }

            @Override
            public int size()
            {
                return list.size();
            }
        };
    }

    public static <TCastTo, TCastFrom> List<TCastTo> cast(final List<TCastFrom> list)
    {
        return new AbstractList<TCastTo>() {
            @Override
            public TCastTo get(int i)
            {
                return (TCastTo)list.get(i);
            }

            @Override
            public int size()
            {
                return list.size();
            }
        };
    }
}

आप castसूची में नेत्रहीन रूप से डाली वस्तुओं का उपयोग कर सकते हैं और convertसुरक्षित कास्टिंग के लिए विधि का उपयोग कर सकते हैं । उदाहरण:

void test(List<TestA> listA, List<TestB> listB)
{
    List<TestB> castedB = ListUtil.cast(listA); // all items are blindly casted
    List<TestB> convertedB = ListUtil.<TestB, TestA>convert(listA); // wrong cause TestA does not extend TestB
    List<TestA> convertedA = ListUtil.<TestA, TestB>convert(listB); // OK all items are safely casted
}

4

केवल एक ही तरीका मुझे पता है:

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

2
काफी अजीब मुझे लगता है कि यह एक संकलक चेतावनी नहीं देता है।
सिमोन फोर्सबर्ग

यह सरणियों के साथ एक विषमता है। आह, जावा सरणियाँ इतनी बेकार हैं। लेकिन, मुझे लगता है कि अन्य उत्तरों की तुलना में यह निश्चित रूप से बदतर नहीं है, और बहुत पठनीय है। मुझे नहीं लगता कि अब कलाकारों की तुलना में असुरक्षित है।
Thufir

3

जब आप एक वस्तु संदर्भ डालते हैं तो आप केवल संदर्भ के प्रकार को कास्टिंग कर रहे हैं, वस्तु के प्रकार को नहीं। कास्टिंग ऑब्जेक्ट के वास्तविक प्रकार को नहीं बदलेगा।

जावा में ऑब्जेक्ट प्रकार परिवर्तित करने के लिए निहित नियम नहीं हैं। (आदिम के विपरीत)

इसके बजाय आपको एक प्रकार को दूसरे में बदलने और इसे मैन्युअल रूप से कॉल करने की आवश्यकता है।

public class TestA {}
public class TestB extends TestA{ 
    TestB(TestA testA) {
        // build a TestB from a TestA
    }
}

List<TestA> result = .... 
List<TestB> data = new List<TestB>();
for(TestA testA : result) {
   data.add(new TestB(testA));
}

यह प्रत्यक्ष समर्थन वाली भाषा की तुलना में अधिक क्रिया है, लेकिन यह काम करती है और आपको इसे बहुत बार करने की आवश्यकता नहीं है।


2

यदि आपके पास कक्षा की कोई वस्तु है TestA, तो आप उसे नहीं डाल सकते TestB। हर TestBएक है TestA, लेकिन दूसरा तरीका नहीं है।

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

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

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

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

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

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


1
मुझे लगता है कि वह कुछ इस तरह की बात कर रहा है, आपको 'a' एक विधि तर्क के रूप में मिलता है जो कि बनाया जाता है - TestA a = new TestB (), तो आप TestB ऑब्जेक्ट प्राप्त करना चाहते हैं और फिर आपको इसे डालना होगा - TestB b = ( टेस्टबी) ए;
संस्कारम

2

आप ग्रहण संग्रहों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);

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

नोट: मैं एक्लिप्स कलेक्शंस के लिए कमिट हूं।


1

यह प्रकार के क्षरण के कारण संभव है। तुम वही पाओगे

List<TestA> x = new ArrayList<TestA>();
List<TestB> y = new ArrayList<TestB>();
x.getClass().equals(y.getClass()); // true

आंतरिक रूप से दोनों सूची प्रकार की हैं List<Object>। उस कारण से आप एक दूसरे को नहीं डाल सकते हैं - कुछ भी नहीं है।


"कास्ट करने के लिए कुछ भी नहीं है" और "आप एक से दूसरे को नहीं डाल सकते हैं" एक विरोधाभास का एक सा है। Integer j = 42; Integer i = (Integer) j;ठीक काम करता है, दोनों पूर्णांक हैं, दोनों में एक ही वर्ग है, इसलिए "कुछ भी नहीं है"।
साइमन फोर्सबर्ग

1

समस्या यह है कि यदि आपके पास TestB है तो TestA की सूची वापस नहीं आती है, इसलिए यदि यह सही ढंग से टाइप किया गया हो तो क्या होगा? फिर यह कास्ट:

class TestA{};
class TestB extends TestA{};
List<? extends TestA> listA;
List<TestB> listB = (List<TestB>) listA;

के रूप में के रूप में अच्छी तरह से आप के लिए आशा कर सकते हैं के बारे में काम करता है (ग्रहण आपको एक अनियंत्रित कलाकारों की चेतावनी देता है जो वास्तव में आप क्या कर रहे हैं, इसलिए meh)। तो क्या आप इसका उपयोग अपनी समस्या को हल करने के लिए कर सकते हैं? वास्तव में आप इसकी वजह हो सकते हैं:

List<TestA> badlist = null; // Actually contains TestBs, as specified
List<? extends TestA> talist = badlist;  // Umm, works
List<TextB> tblist = (List<TestB>)talist; // TADA!

वास्तव में आपने क्या मांगा था, है ना? या वास्तव में सटीक होने के लिए:

List<TestB> tblist = (List<TestB>)(List<? extends TestA>) badlist;

मेरे लिए बस ठीक संकलन करने के लिए लगता है।


1
class MyClass {
  String field;

  MyClass(String field) {
    this.field = field;
  }
}

@Test
public void testTypeCast() {
  List<Object> objectList = Arrays.asList(new MyClass("1"), new MyClass("2"));

  Class<MyClass> clazz = MyClass.class;
  List<MyClass> myClassList = objectList.stream()
      .map(clazz::cast)
      .collect(Collectors.toList());

  assertEquals(objectList.size(), myClassList.size());
  assertEquals(objectList, myClassList);
}

इस परीक्षण से पता चलता है कि किस तरह कास्ट List<Object>करना है List<MyClass>। लेकिन आपको इस बात पर ध्यान देने की आवश्यकता है कि objectListइसमें उसी प्रकार के उदाहरण शामिल होने चाहिए जैसे कि MyClass। और इस उदाहरण पर विचार किया जा सकता है जब List<T>इसका उपयोग किया जाता है। इस प्रयोजन के लिए Class<T> clazzकंस्ट्रक्टर में फ़ील्ड प्राप्त करें और इसके बजाय इसका उपयोग करें MyClass.class


0

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

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

1
यह एक अनावश्यक प्रतिलिपि बनाता है।
डिफान

0

काफी अजीब है कि मैन्युअल रूप से एक सूची डालना अभी भी कुछ उपकरण बॉक्स द्वारा प्रदान नहीं किया गया है जैसे कि कुछ को लागू करना:

@SuppressWarnings({ "unchecked", "rawtypes" })
public static <T extends E, E> List<T> cast(List<E> list) {
    return (List) list;
}

बेशक, यह एक-एक करके वस्तुओं की जांच नहीं करेगा, लेकिन ठीक यही है कि हम यहां से बचना चाहते हैं, अगर हम अच्छी तरह जानते हैं कि हमारा कार्यान्वयन केवल उप-प्रकार प्रदान करता है।

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