'E', 'T', और '?' में क्या अंतर है? जावा जेनरिक के लिए


261

मुझे इस तरह जावा कोड आता है:

public interface Foo<E> {}

public interface Bar<T> {}

public interface Zar<?> {}

उपरोक्त तीनों में क्या अंतर है और वे जावा में इस प्रकार के वर्ग या इंटरफ़ेस घोषणाओं को क्या कहते हैं?


1
मुझे संदेह है कि कोई अंतर है। मुझे लगता है कि यह उस प्रकार के पैरामीटर के लिए सिर्फ एक नाम है। और क्या आखिरी भी वैध है?
कोडइन्चोस

जवाबों:


231

वैसे पहले दो में कोई अंतर नहीं है - वे केवल प्रकार के पैरामीटर ( Eया T) के लिए अलग-अलग नामों का उपयोग कर रहे हैं ।

तीसरा एक वैध घोषणा नहीं है - ?एक वाइल्डकार्ड के रूप में उपयोग किया जाता है जो एक प्रकार का तर्क प्रदान करते समय उपयोग किया जाता है , उदाहरण का List<?> foo = ...अर्थ है कि fooकुछ प्रकार की सूची को संदर्भित करता है, लेकिन हम नहीं जानते कि क्या।

यह सब जेनरिक है , जो एक बहुत बड़ा विषय है। आप निम्नलिखित संसाधनों के माध्यम से इसके बारे में जानना चाह सकते हैं, हालांकि इसमें अधिक पाठ्यक्रम उपलब्ध हैं:


1
ऐसा लगता है कि पीडीएफ का लिंक टूट गया है। मैंने पाया है कि यहाँ एक प्रति प्रतीत होती है , लेकिन मैं 100% निश्चित नहीं हो सकता क्योंकि मुझे नहीं पता कि मूल क्या दिखता था।
जॉन

2
@ जॉन: हाँ, यह एक है। एक लिंक को संपादित करेगा, चाहे वह एक हो या ओरेकल एक ...
जॉन स्कीट

क्या टी, ई और के अलावा कुछ है? जेनरिक में उपयोग किया जाता है? यदि ऐसा है तो वे क्या हैं और उनका क्या मतलब है?
sofs1

1
@ sofs1: इसके बारे में कुछ खास नहीं है Tऔर Eवे सिर्फ पहचानकर्ता हैं। आप KeyValuePair<K, V>उदाहरण के लिए लिख सकते हैं । ?हालांकि विशेष अर्थ है।
जॉन स्कीट

215

यह कुछ और की तुलना में अधिक सम्मेलन है।

  • T एक प्रकार होने का मतलब है
  • Eएक तत्व होने का मतलब है List<E>: ( तत्वों की एक सूची)
  • Kकुंजी (एक में Map<K,V>) है
  • V मान है (वापसी मान या मैप किया गया मान के रूप में)

वे पूरी तरह से विनिमेय हैं (एक ही घोषणा के बावजूद संघर्ष)।


20
<> के बीच का अक्षर सिर्फ एक नाम है। आप अपने उत्तर में जो कुछ भी वर्णन करते हैं, वह सिर्फ सम्मेलन हैं। यह भी एक एकल, ऊपरी मामले पत्र होना जरूरी नहीं है; आप किसी भी नाम का उपयोग कर सकते हैं जो आपको पसंद है, जैसे आप कक्षाएं, चर आदि दे सकते हैं।
जेसपर


6
आपने प्रश्न चिह्न की व्याख्या नहीं की। Downvoted।
शिंज़ो

129

पिछले उत्तर प्रकार पैरामीटर (टी, ई, आदि) की व्याख्या करते हैं, लेकिन वाइल्डकार्ड, "?", या उनके बीच के अंतरों की व्याख्या नहीं करते हैं, इसलिए मैं इसे संबोधित करूंगा।

सबसे पहले, बस स्पष्ट होने के लिए: वाइल्डकार्ड और टाइप पैरामीटर समान नहीं हैं। जहाँ प्रकार पैरामीटर एक प्रकार के चर को परिभाषित करते हैं (उदाहरण के लिए, T) जो एक दायरे के लिए प्रकार का प्रतिनिधित्व करता है, वाइल्डकार्ड नहीं करता है: वाइल्डकार्ड सिर्फ स्वीकार्य प्रकार के एक सेट को परिभाषित करता है जिसे आप एक सामान्य प्रकार के लिए उपयोग कर सकते हैं। बिना किसी बाउंडिंग के ( extendsयाsuper ) के , वाइल्डकार्ड का अर्थ है "यहां किसी भी प्रकार का उपयोग करें"।

वाइल्डकार्ड हमेशा कोण कोष्ठक के बीच आता है, और इसका केवल एक सामान्य प्रकार के संदर्भ में अर्थ होता है:

public void foo(List<?> listOfAnyType) {...}  // pass a List of any type

कभी नहीँ

public <?> ? bar(? someType) {...}  // error. Must use type params here

या

public class MyGeneric ? {      // error
    public ? getFoo() { ... }   // error
    ...
}

यह अधिक भ्रमित हो जाता है जहां वे ओवरलैप करते हैं। उदाहरण के लिए:

List<T> fooList;  // A list which will be of type T, when T is chosen.
                  // Requires T was defined above in this scope
List<?> barList;  // A list of some type, decided elsewhere. You can do
                  // this anywhere, no T required.

विधि परिभाषाओं के साथ जो संभव है उसमें बहुत अधिक ओवरलैप है। निम्नलिखित, कार्यात्मक रूप से, समान हैं:

public <T> void foo(List<T> listOfT) {...}
public void bar(List<?> listOfSomething)  {...}

इसलिए, यदि ओवरलैप है, तो एक या दूसरे का उपयोग क्यों करें? कभी-कभी, यह ईमानदारी से सिर्फ शैली है: कुछ लोग कहते हैं कि अगर आपको ज़रूरत नहीं है एक प्रकार के परम की , तो आपको कोड को सरल / अधिक पठनीय बनाने के लिए वाइल्डकार्ड का उपयोग करना चाहिए। एक मुख्य अंतर जो मैंने ऊपर समझाया था: टाइप परमेस एक प्रकार के चर (जैसे, टी) को परिभाषित करते हैं जिसे आप दायरे में कहीं और उपयोग कर सकते हैं; वाइल्डकार्ड नहीं करता है। अन्यथा, टाइप परम और वाइल्डकार्ड के बीच दो बड़े अंतर हैं:

टाइप परम में कई बाउंडिंग कक्षाएं हो सकती हैं; वाइल्डकार्ड नहीं कर सकता:

public class Foo <T extends Comparable<T> & Cloneable> {...}

वाइल्डकार्ड में कम सीमाएं हो सकती हैं; टाइप परम नहीं कर सकते:

public void bar(List<? super Integer> list) {...}

वाइल्डकार्ड पर निचली सीमा के रूप में ऊपर List<? super Integer>परिभाषित Integerमें, जिसका अर्थ है कि सूची प्रकार पूर्णांक या सुपर-प्रकार का पूर्णांक होना चाहिए। जेनेरिक प्रकार बाउंडिंग से परे है जो मैं विस्तार से कवर करना चाहता हूं। संक्षेप में, यह आपको यह परिभाषित करने की अनुमति देता है कि जेनेरिक प्रकार कौन से प्रकार के हो सकते हैं। इससे जेनेरिकों का बहुरूपता से व्यवहार करना संभव हो जाता है। जैसे:

public void foo(List<? extends Number> numbers) {...}

आप एक पारित कर सकते हैं List<Integer>, List<Float>, List<Byte>, आदि के लिए numbers। टाइप बाउंडिंग के बिना, यह काम नहीं करेगा - यही कारण है कि जेनरिक कैसे हैं।

अंत में, यहाँ एक विधि परिभाषा है जो वाइल्डकार्ड का उपयोग कुछ ऐसा करने के लिए करती है जो मुझे नहीं लगता कि आप किसी अन्य तरीके से कर सकते हैं:

public static <T extends Number> void adder(T elem, List<? super Number> numberSuper) {
    numberSuper.add(elem);
}

numberSuperसंख्या या संख्या के किसी भी प्रकार का उदाहरण हो सकता है (जैसे List<Object>), और elemसंख्या या कोई उपप्रकार होना चाहिए। सभी बाउंडिंग के साथ, कंपाइलर निश्चित हो सकता है कि .add()टाइपसेफ़ है।


"सार्वजनिक शून्य फ़ू (सूची <? फैली हुई संख्या> संख्याएँ) {...}" "का विस्तार" "सुपर" होना चाहिए?
1a1a11a

1
नहीं। उस उदाहरण का बिंदु एक हस्ताक्षर दिखाना है जो बहुरूपिक संख्या की सूची और संख्या के उप-प्रकारों का समर्थन करता है। इसके लिए, आप "फैली हुई" का उपयोग करते हैं। Ie, "मुझे संख्या की एक सूची या कुछ भी जो संख्या बढ़ाता है पास करें" (सूची <पूर्णांक>, सूची <नाव>, जो भी हो)। इस तरह की एक विधि फिर सूची के माध्यम से पुनरावृत्ति कर सकती है और, प्रत्येक तत्व के लिए, "ई", निष्पादित, उदाहरण के लिए, e.floatValue ()। इससे कोई फर्क नहीं पड़ता है कि आपके द्वारा पास किए गए नंबर का उप-प्रकार (एक्सटेंशन) - आप हमेशा ".floatValue ()" में सक्षम होंगे, क्योंकि .floatValue () नंबर की एक विधि है।
हॉकआई पार्कर

अपने अंतिम उदाहरण पर, "सूची <? सुपर नंबर>" बस "सूची <नंबर>" हो सकता है क्योंकि विधि कुछ भी अधिक सामान्य अनुमति नहीं देती है।
जेसाराह

@ जजसरा नप। शायद मेरा उदाहरण अस्पष्ट है, लेकिन मैं उदाहरण में उल्लेख करता हूं कि योजक () एक सूची ले सकता है <ऑब्जेक्ट> (ऑब्जेक्ट संख्या का एक सुपरक्लास है)। यदि आप चाहते हैं कि यह ऐसा करने में सक्षम हो, तो उसके पास हस्ताक्षर "सूची <? सुपर नंबर>" होना चाहिए। यहाँ बिल्कुल "सुपर" की बात है।
हॉकी पार्कर

2
वाइल्डकार्ड और टाइप मापदंडों के बीच अंतर को समझाने में यह उत्तर बहुत अच्छा है, इस उत्तर के साथ एक समर्पित प्रश्न होना चाहिए। मैं हाल ही में जेनेरिक में अधिक गहराई से जा रहा हूं और इस जवाब ने मुझे चीजों को एक साथ रखने में बहुत मदद की, संक्षेप में कई सटीक infos, धन्यवाद!
टेस्टो टेस्टिनी

27

एक प्रकार का चर, <T>, आपके द्वारा निर्दिष्ट कोई भी गैर-आदिम प्रकार हो सकता है: कोई भी वर्ग प्रकार, कोई इंटरफ़ेस प्रकार, कोई सरणी प्रकार या यहां तक ​​कि अन्य प्रकार का चर।

सबसे अधिक इस्तेमाल किया जाने वाला टाइप पैरामीटर नाम हैं:

  • ई - तत्व (बड़े पैमाने पर जावा कलेक्शंस फ्रेमवर्क द्वारा उपयोग किया जाता है)
  • के - कुंजी
  • एन - नंबर
  • टी - प्रकार
  • वि - मान

जावा 7 में इसे इस तरह से इंस्टेंट करने की अनुमति है:

Foo<String, Integer> foo = new Foo<>(); // Java 7
Foo<String, Integer> foo = new Foo<String, Integer>(); // Java 6

3

सबसे अधिक इस्तेमाल किया जाने वाला टाइप पैरामीटर नाम हैं:

E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types

आप इन नामों को जावा एसई एपीआई में इस्तेमाल करते देखेंगे


2

कंपाइलर प्रत्येक वाइल्डकार्ड के लिए एक कैप्चर करेगा (जैसे, सूची में प्रश्न चिह्न) जब यह एक फ़ंक्शन बनाता है जैसे:

foo(List<?> list) {
    list.put(list.get()) // ERROR: capture and Object are not identical type.
}

हालाँकि V जैसा सामान्य प्रकार ठीक रहेगा और इसे एक सामान्य विधि बना देगा :

<V>void foo(List<V> list) {
    list.put(list.get())
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.