जेनेरिक विधियों का उपयोग कब करें और वाइल्ड-कार्ड का उपयोग कब करें?


122

मैं OracleDocGenericMethod से जेनेरिक विधियों के बारे में पढ़ रहा हूं । मैं तुलना के बारे में बहुत भ्रमित हूं जब यह कहता है कि वाइल्ड-कार्ड का उपयोग कब करना है और जेनेरिक विधियों का उपयोग कब करना है। दस्तावेज़ से उद्धृत करना।

interface Collection<E> {
    public boolean containsAll(Collection<?> c);
    public boolean addAll(Collection<? extends E> c);
}

हम इसके बजाय यहाँ सामान्य तरीके इस्तेमाल कर सकते हैं:

interface Collection<E> {
    public <T> boolean containsAll(Collection<T> c);
    public <T extends E> boolean addAll(Collection<T> c);
    // Hey, type variables can have bounds too!
}

[...] यह हमें बताता है कि बहुरूपता के लिए प्रकार तर्क का उपयोग किया जा रहा है; इसका एकमात्र प्रभाव विभिन्न तर्क स्थलों पर विभिन्न प्रकार के वास्तविक तर्क प्रकारों का उपयोग करने की अनुमति देना है। यदि ऐसा है, तो वाइल्डकार्ड का उपयोग करना चाहिए। वाइल्डकार्ड्स को लचीले सबटाइपिंग का समर्थन करने के लिए डिज़ाइन किया गया है, जो कि हम यहां व्यक्त करने की कोशिश कर रहे हैं।

क्या हमें नहीं लगता कि वाइल्ड कार्ड (Collection<? extends E> c);भी इस तरह के बहुरूपता का समर्थन कर रहा है? फिर इसमें जेनेरिक पद्धति के उपयोग को अच्छा क्यों नहीं माना जाता है?

आगे जारी है, यह बताता है,

जेनेरिक विधियाँ एक या अधिक प्रकार के तर्कों के बीच निर्भरता को व्यक्त करने के लिए एक प्रकार के मापदंडों का उपयोग करने की अनुमति देती हैं और / या इसके वापसी प्रकार को। यदि इस तरह की निर्भरता नहीं है, तो जेनेरिक विधि का उपयोग नहीं किया जाना चाहिए।

इसका क्या मतलब है?

उन्होंने उदाहरण प्रस्तुत किया है

class Collections {
    public static <T> void copy(List<T> dest, List<? extends T> src) {
    ...
}

[...]

हम इस तरीके के लिए एक और तरीका लिख ​​सकते हैं, बिना वाइल्डकार्ड का उपयोग किए:

class Collections {
    public static <T, S extends T> void copy(List<T> dest, List<S> src) {
    ...
}

दस्तावेज़ दूसरी घोषणा को हतोत्साहित करता है और पहले सिंटैक्स के उपयोग को बढ़ावा देता है? पहली और दूसरी घोषणा में क्या अंतर है? लगता है दोनों एक ही काम कर रहे हैं?

क्या कोई इस क्षेत्र पर प्रकाश डाल सकता है।

जवाबों:


173

कुछ निश्चित स्थान हैं, जहां वाइल्डकार्ड और टाइप पैरामीटर एक ही काम करते हैं। लेकिन कुछ निश्चित स्थान भी हैं, जहाँ आपको टाइप मापदंडों का उपयोग करना होगा।

  1. यदि आप विभिन्न प्रकार के विधि तर्कों पर कुछ संबंध लागू करना चाहते हैं, तो आप वाइल्डकार्ड के साथ ऐसा नहीं कर सकते, आपको प्रकार के मापदंडों का उपयोग करना होगा।

उदाहरण के लिए अपनी विधि को लेते हुए, मान लें कि आप यह सुनिश्चित करना चाहते हैं कि विधि के लिए पारित सूची srcऔर destसूची copy()एक ही पैरामीटर प्रकार की होनी चाहिए, आप इसे इस तरह के प्रकार के मापदंडों के साथ कर सकते हैं:

public static <T extends Number> void copy(List<T> dest, List<T> src)

यहाँ, आप दोनों कि यह सुनिश्चित किया जाता है destऔर srcके लिए एक ही पैरामिट्रीकृत प्रकार है List। इसलिए, तत्वों को कॉपी करना सुरक्षित srcहै dest

लेकिन, यदि आप वाइल्डकार्ड का उपयोग करने के लिए विधि को बदलते हैं:

public static void copy(List<? extends Number> dest, List<? extends Number> src)

यह अपेक्षा के अनुरूप काम नहीं करेगा। दूसरे मामले में, आप पास List<Integer>और के List<Float>रूप में destऔर कर सकते हैं src। तो, से तत्वों चलती srcकरने के लिए destअब और सुरक्षित नहीं टाइप किया जाएगा। यदि आपको इस तरह के संबंध की आवश्यकता नहीं है, तो आप इस प्रकार के मापदंडों का उपयोग नहीं करने के लिए स्वतंत्र हैं।

वाइल्डकार्ड और प्रकार के मापदंडों का उपयोग करने के बीच कुछ अन्य अंतर हैं:

  • यदि आपके पास केवल एक पैरामीटर प्रकार का तर्क है, तो आप वाइल्डकार्ड का उपयोग कर सकते हैं, हालांकि टाइप पैरामीटर भी काम करेगा।
  • टाइप पैरामीटर कई सीमा का समर्थन करते हैं, वाइल्डकार्ड नहीं।
  • वाइल्डकार्ड ऊपरी और निचले दोनों प्रकार की सीमाओं का समर्थन करते हैं, प्रकार पैरामीटर बस ऊपरी सीमा का समर्थन करते हैं। इसलिए, यदि आप एक विधि को परिभाषित करना चाहते हैं जो एक Listप्रकार का है Integerया यह सुपर क्लास है, तो आप कर सकते हैं:

    public void print(List<? super Integer> list)  // OK

    लेकिन आप टाइप पैरामीटर का उपयोग नहीं कर सकते:

     public <T super Integer> void print(List<T> list)  // Won't compile

संदर्भ:


1
यह अजीब जवाब है। यह स्पष्ट नहीं करता है कि आपको क्यों उपयोग करने की आवश्यकता है ?। आप इसे 'सार्वजनिक स्थैतिक <T1 संख्या के रूप में फिर से लिख सकते हैं, T2 संख्या> शून्य प्रति (सूची <T1> भाग्य, सूची <T2> src) का विस्तार करता है और इस मामले में यह स्पष्ट हो जाता है कि क्या चल रहा है।
कान

@kan। खैर यह असली मुद्दा है। आप एक ही प्रकार को लागू करने के लिए टाइप पैरामीटर का उपयोग कर सकते हैं, लेकिन आप वाइल्डकार्ड के साथ ऐसा नहीं कर सकते। टाइप पैरामीटर के लिए दो अलग-अलग प्रकारों का उपयोग करना अलग बात है।
रोहित जैन

1
@benz। आप एक Listप्रकार के पैरामीटर में निचले सीमा को परिभाषित नहीं कर सकते । List<T super Integer>मान्य नहीं है, और संकलन नहीं होगा।
रोहित जैन

2
@benz। आपका स्वागत है :) मैं आपको सुझाव देता हूं कि मैं अंत में आपके द्वारा पोस्ट किए गए लिंक पर जाऊं। यह जेनरिक पर सबसे अच्छा संसाधन है जो आपको मिलेगा।
रोहित जैन

3
@ jorgen.ringen <T extends X & Y>-> कई सीमाएँ।
रोहित जैन

12

जेम्स गोसलिंग 4 वें संस्करण के जावा प्रोग्रामिंग से नीचे दिए गए उदाहरण पर विचार करें जहां हम 2 सिंगनलिंक्यू को मर्ज करना चाहते हैं:

public static <T1, T2 extends T1> void merge(SinglyLinkQueue<T1> d, SinglyLinkQueue<T2> s){
    // merge s element into d
}

public static <T> void merge(SinglyLinkQueue<T> d, SinglyLinkQueue<? extends T> s){
        // merge s element into d
}

उपरोक्त दोनों विधियों में समान कार्यक्षमता है। तो कौन सा बेहतर है? उत्तर २ है। लेखक के अपने शब्दों में:

"सामान्य नियम वाइल्डकार्ड का उपयोग करना है जब आप कर सकते हैं क्योंकि वाइल्डकार्ड के साथ कोड आमतौर पर कई प्रकार के मापदंडों के साथ कोड की तुलना में अधिक पठनीय होता है। यदि आपको किसी प्रकार के चर की आवश्यकता होती है, तो यह निर्णय लेते हुए, अपने आप से पूछें कि क्या उस प्रकार के चर का उपयोग दो या अधिक मापदंडों से संबंधित है। या रिटर्न प्रकार के साथ एक पैरामीटर प्रकार से संबंधित करने के लिए। यदि उत्तर नहीं है, तो एक वाइल्डकार्ड पर्याप्त होना चाहिए। "

नोट: पुस्तक में केवल दूसरी विधि दी गई है और टाइप पैरामीटर नाम 'T' के बजाय S है। पहला तरीका किताब में नहीं है।


मैंने एक पुस्तक के उद्धरण के लिए मतदान किया, यह प्रत्यक्ष और संक्षिप्त है
कुरपिका

9

अपने पहले प्रश्न में: इसका अर्थ है कि यदि पैरामीटर्स प्रकार और विधि के रिटर्न प्रकार के बीच कोई संबंध है तो एक जेनेरिक का उपयोग करें।

उदाहरण के लिए:

public <T> T giveMeMaximum(Collection<T> items);
public <T> Collection<T> applyFilter(Collection<T> items);

यहां आप कुछ निश्चित मानदंडों का पालन करते हुए कुछ टी निकाल रहे हैं। यदि टी है तो Longआपके तरीके वापस आ जाएंगे Longऔर Collection<Long>; वास्तविक वापसी प्रकार पैरामीटर प्रकार पर निर्भर है, इस प्रकार यह उपयोगी है, और सलाह दी जाती है, जेनेरिक प्रकारों का उपयोग करने के लिए।

जब यह मामला नहीं है तो आप वाइल्ड कार्ड प्रकारों का उपयोग कर सकते हैं:

public int count(Collection<?> items);
public boolean containsDuplicate(Collection<?> items);

इस दो उदाहरणों में जो कुछ भी संग्रह में आइटम के प्रकार वापसी के प्रकार होंगे intऔर boolean

आपके उदाहरणों में:

interface Collection<E> {
    public boolean containsAll(Collection<?> c);
    public boolean addAll(Collection<? extends E> c);
}

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

दूसरा सवाल:

class Collections {
    public static <T> void copy(List<T> dest, List<? extends T> src) {
    ...
}

यह पहला कोड आपको List<? extends T> srcएक पैरामीटर के रूप में विषम पारित करने की अनुमति देता है । इस सूची में विभिन्न वर्गों के कई तत्व शामिल हो सकते हैं जब तक कि वे सभी आधार वर्ग टी का विस्तार करते हैं।

अगर आप के पास था:

interface Fruit{}

तथा

class Apple implements Fruit{}
class Pear implements Fruit{}
class Tomato implements Fruit{}

तुम यह कर सकते थे

List<? extends Fruit> basket = new ArrayList<? extends Fruit>();
basket.add(new Apple());
basket.add(new Pear());
basket.add(new Tomato());
List<Fruit> fridge = new ArrayList<Fruit>(); 

Collections.copy(fridge, basket);// works 

दूसरी ओर

class Collections {
    public static <T, S extends T> void copy(List<T> dest, List<S> src) {
    ...
}

List<S> srcएक विशेष वर्ग S के लिए विवश होना कि T का एक उपवर्ग है। सूची में केवल एक वर्ग के तत्व शामिल हो सकते हैं (इस उदाहरण S में) और कोई अन्य वर्ग नहीं, भले ही वे T को लागू करें। आप मेरे पिछले उदाहरण का उपयोग नहीं कर पाएंगे लेकिन आप कर सकते हैं:

List<Apple> basket = new ArrayList<Apple>();
basket.add(new Apple());
basket.add(new Apple());
basket.add(new Apple());
List<Fruit> fridge = new ArrayList<Fruit>();

Collections.copy(fridge, basket); /* works since the basket is defined as a List of apples and not a list of some fruits. */

1
List<? extends Fruit> basket = new ArrayList<? extends Fruit>();मान्य सिंटैक्स नहीं है। आपको बिना किसी सीमा के ArrayList को तुरंत करना होगा।
अर्नाल्ड पिस्टोरियस

ऊपर दिए गए उदाहरण में टोकरी में एक सेब नहीं जोड़ा जा सकता क्योंकि टोकरी नाशपाती की सूची हो सकती है '। गलत उदाहरण AFAIK। और साथ ही संकलन नहीं करता है।
खन्ना ११

1
@AnnoldPistorius मुझे भ्रमित करते हैं। मैंने ArrayList के एपीआई प्रलेखन की जाँच की और इसमें एक निर्माता के हस्ताक्षर हैं ArrayList(Collection<? extends E> c)। क्या आप मुझे समझा सकते हैं कि आपने ऐसा क्यों कहा है?
कुरापिका

@ कुरुपिका हो सकती है कि मैं एक पुराने जावा संस्करण का उपयोग कर रहा था? टिप्पणी लगभग 3 साल पहले पोस्ट की गई थी।
अर्नोल्ड पिस्टोरियस

2

वाइल्डकार्ड विधि भी सामान्य है - आप इसे कुछ प्रकारों के साथ कह सकते हैं।

<T>वाक्य रचना एक प्रकार चर नाम को परिभाषित करता है। यदि किसी प्रकार के चर का कोई उपयोग होता है (जैसे कि विधि कार्यान्वयन में या अन्य प्रकार के लिए एक बाधा के रूप में), तो यह इसे नाम देने के लिए समझ में आता है, अन्यथा आप ?अनाम चर के रूप में उपयोग कर सकते हैं । तो, बस एक शॉर्ट-कट की तरह दिखता है।

इसके अलावा, ?जब आप एक क्षेत्र की घोषणा करते हैं तो वाक्यविन्यास टालने योग्य नहीं होता है:

class NumberContainer
{
 Set<? extends Number> numbers;
}

3
क्या यह एक टिप्पणी नहीं है?
बुआके सिंडी

@BuhakeSindi क्षमा करें, क्या अस्पष्ट है? क्यों -1 मुझे लगता है कि यह सवाल का जवाब देता है।
कान

2

मैं एक-एक करके आपके सवाल का जवाब देने की कोशिश करूंगा।

क्या हमें नहीं लगता कि वाइल्ड कार्ड (Collection<? extends E> c);भी इस तरह के बहुरूपता का समर्थन कर रहा है?

नहीं, इसका कारण यह है कि बंधे वाइल्डकार्ड में कोई परिभाषित पैरामीटर प्रकार नहीं है। यह एक अज्ञात है। यह सब "जानता है" यह है कि "रोकथाम" एक प्रकार का है E(जो भी परिभाषित किया गया है)। इसलिए, यह सत्यापित नहीं कर सकता है कि क्या प्रदान किया गया मूल्य बंधे हुए प्रकार से मेल खाता है।

इसलिए, वाइल्डकार्ड पर बहुरूपी व्यवहार करना कोई समझदारी नहीं है।

दस्तावेज़ दूसरी घोषणा को हतोत्साहित करता है और पहले सिंटैक्स के उपयोग को बढ़ावा देता है? पहली और दूसरी घोषणा में क्या अंतर है? लगता है दोनों एक ही काम कर रहे हैं?

इस मामले में पहला विकल्प बेहतर है क्योंकि Tहमेशा बाध्य होता है, और sourceनिश्चित रूप से मान (अज्ञात का) होगा जो उपवर्ग करता है T

तो, मान लीजिए कि आप सभी नंबरों की सूची की प्रतिलिपि बनाना चाहते हैं, तो पहला विकल्प होगा

Collections.copy(List<Number> dest, List<? extends Number> src);

src, अनिवार्य रूप से, आदि को स्वीकार कर सकते हैं List<Double>, List<Float>क्योंकि इसमें पाए जाने वाले पैरामीटरकृत प्रकार के लिए एक ऊपरी सीमा है dest

दूसरा विकल्प आपको Sहर उस प्रकार के लिए बाध्य करने के लिए बाध्य करेगा जिसे आप कॉपी करना चाहते हैं, जैसे

//For double 
Collections.copy(List<Number> dest, List<Double> src); //Double extends Number.

//For int
Collections.copy(List<Number> dest, List<Integer> src); //Integer extends Number.

जैसा Sकि एक पैरामीटर प्रकार है जिसे बाध्यकारी की आवश्यकता होती है।

आशा है कि ये आपकी मदद करेगा।


क्या आप बता सकते हैं कि आपके अंतिम पैराग्राफ का क्या मतलब है
बेंज़

जो दूसरा विकल्प बताता है, वह आपको एक को बांधने के लिए मजबूर करेगा ...... क्या आप इसके बारे में विस्तार से
बता

<S extends T>कहा जाता है कि Sएक पैरामीटर प्रकार है जो एक उपवर्ग है T, इसलिए इसके लिए एक मानकीकृत प्रकार (कोई वाइल्डकार्ड) की आवश्यकता नहीं होती है जो उपवर्ग हो T
बुहके सिंडी

2

एक अन्य अंतर जो यहां सूचीबद्ध नहीं है।

static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
    for (T o : a) {
        c.add(o); // correct
    }
}

लेकिन निम्नलिखित संकलन समय त्रुटि में परिणाम होगा।

static <T> void fromArrayToCollection(T[] a, Collection<?> c) {
    for (T o : a) {
        c.add(o); // compile time error
    }
}

0

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

हालाँकि इसके अलावा वाइल्डकार्ड अधिक संक्षिप्त कोड लिखने की सेवा करते हैं, जैसा कि आपके द्वारा उल्लिखित दस्तावेज़ में निम्नलिखित कथनों द्वारा वर्णित है:

जेनेरिक विधियाँ एक या अधिक प्रकार के तर्कों के बीच निर्भरता को व्यक्त करने के लिए एक प्रकार के मापदंडों का उपयोग करने की अनुमति देती हैं और / या इसके वापसी प्रकार को। यदि इस तरह की निर्भरता नहीं है, तो जेनेरिक विधि का उपयोग नहीं किया जाना चाहिए।

[...]

वाइल्डकार्ड का उपयोग स्पष्ट प्रकार के मापदंडों को घोषित करने की तुलना में स्पष्ट और अधिक संक्षिप्त है, और इसलिए जब भी संभव हो इसे प्राथमिकता दी जानी चाहिए।

[...]

वाइल्डकार्ड का यह भी फायदा है कि उनका उपयोग विधि हस्ताक्षरों के बाहर, खेतों के प्रकार, स्थानीय चर और सरणियों के रूप में किया जा सकता है।


0

मुख्य रूप से -> वाइल्डकार्ड गैर-जेनेरिक पद्धति के पैरामीटर / तर्क स्तर पर जेनरिक लागू करते हैं। ध्यान दें। यह सामान्य रूप से जेनेरिकमैथोड में भी किया जा सकता है, लेकिन यहां के बजाय? हम स्वयं T का उपयोग कर सकते हैं।

पैकेज जेनरिक;

public class DemoWildCard {


    public static void main(String[] args) {
        DemoWildCard obj = new DemoWildCard();

        obj.display(new Person<Integer>());
        obj.display(new Person<String>());

    }

    void display(Person<?> person) {
        //allows person of Integer,String or anything
        //This cannnot be done if we use T, because in that case we have to make this method itself generic
        System.out.println(person);
    }

}

class Person<T>{

}

SO वाइल्डकार्ड इस तरह के अपने विशिष्ट यूसेकस हैं।

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