मुझे इस तरह जावा कोड आता है:
public interface Foo<E> {}
public interface Bar<T> {}
public interface Zar<?> {}
उपरोक्त तीनों में क्या अंतर है और वे जावा में इस प्रकार के वर्ग या इंटरफ़ेस घोषणाओं को क्या कहते हैं?
मुझे इस तरह जावा कोड आता है:
public interface Foo<E> {}
public interface Bar<T> {}
public interface Zar<?> {}
उपरोक्त तीनों में क्या अंतर है और वे जावा में इस प्रकार के वर्ग या इंटरफ़ेस घोषणाओं को क्या कहते हैं?
जवाबों:
वैसे पहले दो में कोई अंतर नहीं है - वे केवल प्रकार के पैरामीटर ( E
या T
) के लिए अलग-अलग नामों का उपयोग कर रहे हैं ।
तीसरा एक वैध घोषणा नहीं है - ?
एक वाइल्डकार्ड के रूप में उपयोग किया जाता है जो एक प्रकार का तर्क प्रदान करते समय उपयोग किया जाता है , उदाहरण का List<?> foo = ...
अर्थ है कि foo
कुछ प्रकार की सूची को संदर्भित करता है, लेकिन हम नहीं जानते कि क्या।
यह सब जेनरिक है , जो एक बहुत बड़ा विषय है। आप निम्नलिखित संसाधनों के माध्यम से इसके बारे में जानना चाह सकते हैं, हालांकि इसमें अधिक पाठ्यक्रम उपलब्ध हैं:
T
और E
वे सिर्फ पहचानकर्ता हैं। आप KeyValuePair<K, V>
उदाहरण के लिए लिख सकते हैं । ?
हालांकि विशेष अर्थ है।
यह कुछ और की तुलना में अधिक सम्मेलन है।
T
एक प्रकार होने का मतलब है E
एक तत्व होने का मतलब है List<E>
: ( तत्वों की एक सूची) K
कुंजी (एक में Map<K,V>
) है V
मान है (वापसी मान या मैप किया गया मान के रूप में) वे पूरी तरह से विनिमेय हैं (एक ही घोषणा के बावजूद संघर्ष)।
पिछले उत्तर प्रकार पैरामीटर (टी, ई, आदि) की व्याख्या करते हैं, लेकिन वाइल्डकार्ड, "?", या उनके बीच के अंतरों की व्याख्या नहीं करते हैं, इसलिए मैं इसे संबोधित करूंगा।
सबसे पहले, बस स्पष्ट होने के लिए: वाइल्डकार्ड और टाइप पैरामीटर समान नहीं हैं। जहाँ प्रकार पैरामीटर एक प्रकार के चर को परिभाषित करते हैं (उदाहरण के लिए, 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()
टाइपसेफ़ है।
एक प्रकार का चर, <T>, आपके द्वारा निर्दिष्ट कोई भी गैर-आदिम प्रकार हो सकता है: कोई भी वर्ग प्रकार, कोई इंटरफ़ेस प्रकार, कोई सरणी प्रकार या यहां तक कि अन्य प्रकार का चर।
सबसे अधिक इस्तेमाल किया जाने वाला टाइप पैरामीटर नाम हैं:
जावा 7 में इसे इस तरह से इंस्टेंट करने की अनुमति है:
Foo<String, Integer> foo = new Foo<>(); // Java 7
Foo<String, Integer> foo = new Foo<String, Integer>(); // Java 6
कंपाइलर प्रत्येक वाइल्डकार्ड के लिए एक कैप्चर करेगा (जैसे, सूची में प्रश्न चिह्न) जब यह एक फ़ंक्शन बनाता है जैसे:
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())
}