सूची, सूची <?>, सूची <टी>, सूची <ई>, और सूची <वस्तु> के बीच अंतर


194

क्या अंतर हैं List, List<?>, List<T>, List<E>, और List<Object>?

1. सूची

List: एक कच्चा प्रकार है, इसलिए नहीं typesafe। कास्टिंग खराब होने पर यह केवल रनटाइम त्रुटि उत्पन्न करेगा। हम एक बुरा समय त्रुटि चाहते हैं जब डाली खराब है। उपयोग करने के लिए अनुशंसित नहीं है।

2. सूची <?>

List<?>एक अनबाउंड वाइल्डकार्ड है। लेकिन मुझे यकीन नहीं है कि यह किस लिए है? मैं एक List<?>समस्या के बिना मुद्रित कर सकते हैं :

public static void test(List<?> list){
    System.out.println(list);   // Works
}

मैं आइटम क्यों नहीं जोड़ सकता हूँ List<?>?

public static void test(List<?> list){
    list.add(new Long(2));     // Error
    list.add("2");             // Error
    System.out.println(list);
}

3. सूची <टी>

public static void test(List<T> list){   // T cannot be resolved
    System.out.println(list);
}

मैं इस वाक्यविन्यास को नहीं समझता। मैंने ऐसा कुछ देखा, और यह काम करता है:

public <T> T[] toArray(T[] a){
    return a;   
}

कभी कभी, मैं देख रहा हूँ <T>, या <E>, या <U>, <T,E>। क्या वे सभी समान हैं या वे कुछ अलग करते हैं?

4. सूची <वस्तु>

यह त्रुटि देता है "विधि test(List<Object>)तर्क के लिए लागू नहीं है List<String>":

public static void test(List<Object> list){
    System.out.println(list);
}

अगर मैं यह कोशिश करता हूं तो मुझे "इससे कास्ट नहीं किया जा सकता List<String>है List<Object>":

test((List<Object>) names);

मैं उलझन में हूं। Stringका एक उपवर्ग है Object, तो एक उपवर्ग क्यों नहीं List<String>है List<Object>?

जवाबों:


77

1) सही

2) आप उस के बारे में सोच सकते हैं जैसे "केवल पढ़ने के लिए" सूची, जहां आप आइटम के प्रकार के बारे में परवाह नहीं करते हैं। उदाहरण के लिए एक विधि द्वारा उपयोग किया जाना चाहिए जो सूची की लंबाई वापस कर रहा है।

3) टी, ई और यू एक ही हैं, लेकिन लोग टाइप के लिए ई का उपयोग करते हैं, ई के लिए ई, तत्व के लिए, वी के लिए मूल्य और कुंजी के लिए के। संकलित करने वाली विधि कहती है कि उसने एक निश्चित प्रकार की एक सरणी ली, और उसी प्रकार की एक सरणी लौटाता है।

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


2
+1 केवल सूची को पढ़ने के लिए 2। मैं इसे प्रदर्शित करने के लिए कुछ कोड लिखता हूं 2। tyvm
थांग फाम

लोग क्यों इस्तेमाल List<Object>करेंगे?
थांग फाम

3
यह एक सूची बनाने का एक तरीका है जो किसी भी प्रकार की वस्तुओं को स्वीकार करता है, आप शायद ही कभी इसका उपयोग करते हैं।
काज

वास्तव में मुझे नहीं लगता कि कोई भी इसका उपयोग करते हुए यह विचार करेगा कि आपके पास अब कोई पहचानकर्ता नहीं हो सकता है।
if_zero_equals_one

1
@if_zero_equals_one हाँ, लेकिन फिर आपको एक संकलक चेतावनी मिलेगी (यह चेतावनी देगा और कहेगा कि आप कच्चे प्रकार का उपयोग कर रहे हैं), और आप कभी भी चेतावनी के साथ अपने कोड को संकलित नहीं करना चाहते हैं।
काज

26

अंतिम भाग के लिए: हालांकि स्ट्रिंग ऑब्जेक्ट का एक सबसेट है, लेकिन सूची <स्ट्रिंग> को सूची <ऑब्जेक्ट> से विरासत में नहीं मिला है।


11
बहुत अच्छी बात; कई लोग मानते हैं कि क्योंकि क्लास C, P से विरासत में मिला है, लिस्ट <C> भी लिस्ट <P> से विरासत में मिला है। जैसा कि आपने बताया कि ऐसा नहीं है। इसका कारण यह था कि यदि हम सूची <स्ट्रिंग> से सूची <ऑब्जेक्ट> तक कास्ट कर सकते हैं, तो हम ऑब्जेक्ट को उस सूची में डाल सकते हैं, इस प्रकार किसी तत्व को पुनः प्राप्त करने का प्रयास करते समय सूची <स्ट्रिंग> के मूल अनुबंध का उल्लंघन करते हैं।
पीटर

2
+1। अच्छी बात है। तो लोग इसके लिए उपयोग क्यों करेंगे List<Object>?
थांग फाम

9
सूची <ऑब्जेक्ट> का उपयोग विभिन्न वर्गों की वस्तुओं की सूची को स्टोर करने के लिए किया जा सकता है।
फर्सिड ज़ेकर

20

संकेतन का List<?>अर्थ है "किसी चीज़ की सूची (लेकिन मैं यह नहीं कह रहा हूं कि क्या है)"। चूंकि testसूची में किसी भी प्रकार की वस्तु के लिए कोड काम करता है, यह एक औपचारिक विधि पैरामीटर के रूप में काम करता है।

एक प्रकार के पैरामीटर का उपयोग करना (जैसे आपके बिंदु 3 में), इसके लिए आवश्यक है कि टाइप पैरामीटर घोषित किया जाए। इसके लिए जावा सिंटैक्स <T>को फ़ंक्शन के सामने रखना है। यह विधि निकाय में नामों का उपयोग करने से पहले एक विधि के लिए औपचारिक पैरामीटर नामों की घोषणा करने के लिए बिल्कुल अनुरूप है।

List<Object>एक को स्वीकार नहीं करने के बारे में List<String>, जो समझ में आता है क्योंकि एक Stringनहीं है Object; यह एक उपवर्ग है Object। फिक्स घोषित करना है public static void test(List<? extends Object> set) ...। लेकिन फिर extends Objectबेमानी है, क्योंकि हर वर्ग प्रत्यक्ष या अप्रत्यक्ष रूप से फैली हुई है Object


लोग क्यों इस्तेमाल List<Object>करेंगे?
थांग फाम

10
मुझे लगता है कि "कुछ की एक सूची" एक बेहतर अर्थ है List<?>क्योंकि सूची कुछ विशिष्ट लेकिन अज्ञात प्रकार की है। List<Object>वास्तव में "कुछ भी" की सूची होगी क्योंकि इसमें वास्तव में कुछ भी हो सकता है।
कॉलिनड

1
@ कोलिनड - मेरा मतलब "किसी भी एक चीज" के अर्थ में "कुछ भी" था। लेकिन तुम सही हो; इसका मतलब है, "किसी चीज़ की सूची, लेकिन मैं आपको बताने नहीं जा रहा हूँ"।
टेड हॉप

@ColinD इसका मतलब था कि आपने उनके शब्दों को क्यों दोहराया? हां यह थोड़ा अलग शब्दों के साथ लिखा गया था, लेकिन अर्थ समान है ...
user25

14

कारण आप डाली नहीं कर सकते List<String>करने के लिए List<Object>है कि यह आप की कमी का उल्लंघन करने की अनुमति होगी है List<String>

निम्नलिखित परिदृश्य के बारे में सोचें: यदि मेरे पास एक है List<String>, तो यह केवल प्रकार की वस्तुओं को शामिल करना चाहिए String। (जो एक finalवर्ग है)

अगर मैं उसको कास्ट कर सकता हूं List<Object>, तो यह मुझे Objectउस सूची में जोड़ने की अनुमति देता है , इस प्रकार मूल अनुबंध का उल्लंघन होता है List<String>

इस प्रकार, सामान्य तौर पर, यदि वर्ग वर्ग से Cविरासत में मिला है P, तो आप यह नहीं कह सकते कि यह GenericType<C>भी विरासत में मिला है GenericType<P>

एनबी मैं पहले से ही इस पर पिछले जवाब में टिप्पणी कर चुका था, लेकिन इस पर विस्तार करना चाहता था।


tyvm, मैं आपकी टिप्पणी और आपके उत्तर दोनों को अपलोड करता हूं, क्योंकि यह बहुत अच्छी व्याख्या है। अब लोग कहां और क्यों इस्तेमाल करेंगे List<Object>?
थांग फाम

3
आम तौर पर आपको इसका उपयोग नहीं करना चाहिए List<Object>क्योंकि यह जेनरिक के उद्देश्य को पराजित करता है। हालाँकि, ऐसे मामले हैं जिनमें पुराने कोड को Listस्वीकार करने के विभिन्न प्रकार हो सकते हैं, इसलिए आप कोड को कच्चे प्रकार के लिए संकलक चेतावनी से बचने के लिए बस प्रकार के मानकीकरण का उपयोग करने के लिए वापस लेना चाह सकते हैं। (लेकिन कार्यक्षमता अपरिवर्तित है)
पीटर

5

मैं जावा गूढ़ व्यक्ति को पढ़ने की सलाह दूंगा। यह घोषणाओं में विरासत, जेनरिक, अमूर्तता और वाइल्डकार्ड की व्याख्या करता है। http://www.javapuzzlers.com/


5

आइए हम उनके बारे में जावा इतिहास के संदर्भ में बात करते हैं;

  1. List:

सूची का मतलब है कि इसमें कोई भी वस्तु शामिल हो सकती है। जावा 5.0 से पहले सूची जारी की गई थी; जावा 5.0 ने पिछड़े संगतता के लिए सूची पेश की।

List list=new  ArrayList();
list.add(anyObject);
  1. List<?>:

?अज्ञात वस्तु का मतलब कोई वस्तु नहीं है; वाइल्डकार्ड ?परिचय जेनेरिक प्रकार द्वारा निर्मित समस्या को हल करने के लिए है; वाइल्डकार्ड देखें ; लेकिन यह भी एक और समस्या का कारण बनता है:

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
  1. List< T> List< E>

आपकी परियोजना लिब में कोई भी टी या ई प्रकार के आधार पर सामान्य घोषणा का मतलब नहीं है।

  1. List< Object> सामान्य पैरामीटर का मतलब है।

5

आपके तीसरे बिंदु में, "T" को हल नहीं किया जा सकता है क्योंकि इसकी घोषणा नहीं की गई है, आमतौर पर जब आप एक सामान्य वर्ग की घोषणा करते हैं तो आप "T" को बाध्य प्रकार के पैरामीटर के नाम के रूप में उपयोग कर सकते हैं , कई ऑनलाइन उदाहरण सहित ओरेकल के ट्यूटोरियल "T" का उपयोग करते हैं। प्रकार पैरामीटर का नाम, उदाहरण के लिए कहें, आप एक वर्ग की घोषणा करते हैं जैसे:

public class FooHandler<T>
{
   public void operateOnFoo(T foo) { /*some foo handling code here*/}

}

आप कह रहे हैं कि FooHandler's operateOnFooविधि "T" के एक प्रकार की उम्मीद करती है, जो कि कक्षा घोषणा पर ही घोषित की जाती है, इसे ध्यान में रखते हुए, आप बाद में एक और विधि जोड़ सकते हैं जैसे

public void operateOnFoos(List<T> foos)

या तो T, E या U के सभी मामलों में टाइप पैरामीटर के सभी पहचानकर्ता हैं, तो आपके पास एक से अधिक प्रकार के पैरामीटर भी हो सकते हैं जो कि सिंटैक्स है

public class MyClass<Atype,AnotherType> {}

आपके आगामी समय में यद्यपि प्रभावशाली रूप से स्टिंग एक उप प्रकार की वस्तु है, लेकिन जेनरिक कक्षाओं में ऐसा कोई संबंध List<String>नहीं है , यह एक उप प्रकार नहीं है , List<Object>वे संकलक दृष्टिकोण से दो अलग-अलग प्रकार हैं, यह इस ब्लॉग प्रविष्टि में सबसे अच्छा समझाया गया है


5

सिद्धांत

String[] तक डाली जा सकती है Object[]

परंतु

List<String>नहीं डाला जा सकता List<Object>

अभ्यास

सूचियों के लिए यह उससे कहीं अधिक सूक्ष्म है, क्योंकि संकलन के समय एक विधि में दिए गए सूची पैरामीटर के प्रकार की जाँच नहीं की जाती है। विधि की परिभाषा और साथ ही कह सकती है List<?>- संकलक के दृष्टिकोण से यह समकक्ष है। यही कारण है कि ओपी का उदाहरण # 2 रनटाइम त्रुटियाँ देता है त्रुटियों को संकलित नहीं करता है।

यदि आप किसी List<Object>पैरामीटर को किसी विधि को ध्यान से देखते हैं, तो आप सूची के किसी भी तत्व पर एक प्रकार की जांच को मजबूर नहीं करते हैं, तो आप अपनी पद्धति का उपयोग करके परिभाषित कर सकते हैं List<Object>लेकिन वास्तव List<String>में कॉलिंग कोड से एक पैरामीटर को स्वीकार करते हैं ।

A. तो यह कोड संकलन या रनटाइम त्रुटियों को नहीं देगा और वास्तव में (और शायद आश्चर्यजनक रूप से?) काम करेगा।

public static void main(String[] args) {
    List argsList = new ArrayList<String>();
    argsList.addAll(Arrays.asList(args));
    test(argsList);  // The object passed here is a List<String>
}

public static void test(List<Object> set) {
    List<Object> params = new ArrayList<>();  // This is a List<Object>
    params.addAll(set);       // Each String in set can be added to List<Object>
    params.add(new Long(2));  // A Long can be added to List<Object>
    System.out.println(params);
}

B. यह कोड रनटाइम त्रुटि देगा:

public static void main(String[] args) {
    List argsList = new ArrayList<String>();
    argsList.addAll(Arrays.asList(args));
    test1(argsList);
    test2(argsList);
}

public static void test1(List<Object> set) {
    List<Object> params = set;  // Surprise!  Runtime error
}

public static void test2(List<Object> set) {
    set.add(new Long(2));       // Also a runtime error
}

C. यह कोड रनटाइम त्रुटि देगा ( java.lang.ArrayStoreException: java.util.Collections$UnmodifiableRandomAccessList Object[]):

public static void main(String[] args) {
    test(args);
}

public static void test(Object[] set) {
    Object[] params = set;    // This is OK even at runtime
    params[0] = new Long(2);  // Surprise!  Runtime error
}

बी में, कंपाइलर समय पर पैरामीटर setटाइप नहीं किया जाता है List: कंपाइलर इसे देखता है List<?>। एक रनटाइम त्रुटि है क्योंकि रनटाइम पर, setवास्तविक ऑब्जेक्ट से पारित हो जाता है main(), और यह एक है List<String>। A List<String>को नहीं दिया जा सकता List<Object>

सी में, पैरामीटर setको एक की आवश्यकता है Object[]। जब String[]पैरामीटर के रूप में किसी ऑब्जेक्ट के साथ कॉल किया जाता है तो कोई संकलन त्रुटि और कोई रनटाइम त्रुटि नहीं होती है । ऐसा इसलिए है क्योंकि इसमें String[]डाली जाती है Object[]। लेकिन प्राप्त वास्तविक वस्तु test()एक बनी हुई है String[], यह नहीं बदला। तो paramsवस्तु भी बन जाती है String[]। और तत्व 0 को एक String[]को नहीं सौंपा जा सकता है Long!

(उम्मीद है कि मेरे पास सबकुछ यहीं है, अगर मेरा तर्क गलत है तो मुझे यकीन है कि समुदाय मुझे बताएगा। अद्यतन: मैंने उदाहरण ए में कोड को अपडेट किया है ताकि यह वास्तव में संकलित हो, जबकि अभी भी बना बिंदु दिखा रहा है।)


इसे मैं अपने उदाहरण एक कोशिश की है नहीं करता है काम: List<Object> cannot be applied to List<String>। आप नहीं कर सकते हैं पारित ArrayList<String>एक विधि है कि उम्मीद करने के लिए ArrayList<Object>
पारसेकर

धन्यवाद, तारीख में देरी के बजाय मैंने उदाहरण ए को बदल दिया है ताकि यह अब काम करे। मुख्य रूप से मुख्य () में argsList को मुख्य रूप से परिभाषित करना था।
त्रिज्या

4

समस्या 2 ठीक है, क्योंकि "System.out.println (सेट);" का अर्थ है "System.out.println (set.toString ());" सेट लिस्ट का एक उदाहरण है, इसलिए कम्पलिट लिस्ट को कॉल करेगा। स्ट्रींग ();

public static void test(List<?> set){
set.add(new Long(2)); //--> Error  
set.add("2");    //--> Error
System.out.println(set);
} 
Element ? will not promise Long and String, so complier will  not accept Long and String Object

public static void test(List<String> set){
set.add(new Long(2)); //--> Error
set.add("2");    //--> Work
System.out.println(set);
}
Element String promise it a String, so complier will accept String Object

समस्या 3: ये प्रतीक समान हैं, लेकिन आप उन्हें अलग विनिर्देश दे सकते हैं। उदाहरण के लिए:

public <T extends Integer,E extends String> void p(T t, E e) {}

समस्या 4: संग्रह प्रकार पैरामीटर कोविरियन की अनुमति नहीं देता है। लेकिन सरणी covariance की अनुमति देता है।


0

आप सही हैं: स्ट्रिंग ऑब्जेक्ट का सबसेट है। चूंकि स्ट्रिंग ऑब्जेक्ट की तुलना में अधिक "सटीक" है, इसलिए आपको इसे System.out.println () के तर्क के रूप में उपयोग करने के लिए डालना चाहिए।

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