2017-09-12 7 views
7

मैं जावा में सामान्य प्रकार को समझने की कोशिश कर रहा हूं, और सिद्धांत रूप में यह समझ में आता है, लेकिन जब मुझे इसे वास्तविक कोड पर लागू करने की आवश्यकता है तो मुझे समस्या है। मैं अमूर्त विधि घोषित करना चाहता हूं जो सामान्य प्रकार लौटाएगा। आइए मान लें कि मेरे पास मैजिकेबल नामक कुछ खाली इंटरफ़ेस है और 2 वर्ग इसे लागू करता है: मैजिक और विच। अब मुझे आश्चर्य है कि क्या उन 3 घोषणाओं के बीच अंतर है:वाइल्डकार्ड का उपयोग करने और जावा में अमूर्त विधि में जेनेरिक प्रकार घोषित करने के बीच अंतर

/*1*/protected abstract <T extends Magicable> List<T> getMagicables(); 
/*2*/protected abstract List<? extends Magicable> getMagicables(); 
/*3*/protected abstract List<Magicable> getMagicables(); 
  1. पहले मामले में मैं समस्या है जब मैं कुछ वर्ग जो अमूर्त वर्ग फैली में इस विधि के शरीर लागू करना चाहते हैं:

    प्रकार सुरक्षा: वापसी प्रकार सूची

    मैं चेतावनी संदेश है मैजिकिकन सेवा के प्रकार सेमैजिकेबल > के अनुरूप मैजिकिकन सेवा के लिए मैजिकिकन() के लिए मैग्निक > मैकिकन सेवा के लिए अनचेक रूपांतरण की आवश्यकता है।

  2. दूसरे मामले में मैं इस चेतावनी की जरूरत नहीं है, लेकिन मैं जिसमें मैं सार विधि से ऊपर घोषित सार कक्षा में समस्या है:

    public void <T extends Magicable> T getOneFromList() { 
         List<T> list = getMagicables(); 
         //..... 
        } 
    

    इस मामले में मैं getMagicables में संकलन त्रुटि है () कॉल:

    टाइप मिस्चैच: सूची < कैप्चर # 2-से कनवर्ट नहीं कर सकता? < टी >

  3. तीसरा मामला दोनों कोड के उपरोक्त स्थानों में संकलन त्रुटियों का कारण बनता सूची में Magicable > फैली हुई है। मुझे नहीं लगता कि यह मेरे मामले में सही समाधान है या नहीं।

+0

'सूची चुड़ैलों = getMagicables (चुड़ैल उपयोग नहीं कर सकते .class); 'करने योग्य है - 1.' (कक्षाके साथप्रकार) .... type.cast (obj) ... '। –

+0

यह बेहद असंभव है कि # 1 विधि का एक रूप है जो कभी भी उपयोग करेगा। चूंकि 'टी' के प्रकार के तर्क को कोड द्वारा दिया गया है जो विधि को कॉल करता है और चूंकि हम 'टी' का नया उदाहरण नहीं बना सकते हैं, इसलिए एकमात्र चीज बिना किसी प्रकार की अनचेक कास्ट के कर सकती है, एक खाली सूची लौटाती है । इसलिए क्यों उदा। ['संग्रह .emptyList'] (http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#emptyList--) और इसी तरह के एकमात्र स्थान हैं जिन्हें आप पाएंगे तरीका। – Radiodef

उत्तर

2
  1. पहला मामला

बस के साथ अपने प्रणाली की घोषणा: क्या तुम सच में चाहते हैं

@Override 
    protected <T extends Magicable> List<T> getMagicables() { 
     List<T> list = ... 
     return list 
    } 

इस:

@Override 
    protected List<Magican> getMagicable() {..} 

आप घोषित करने के लिए हो सकता है आपके जीन वर्ग defintion

 public abstract class AbstractKlass<T extends Magicable> { 
     protected abstract List<T> getMagicables(); 
    } 
फिर अपने उपवर्ग में

में रिक टी:

 public class MySubClass extends AbstractKlass<Magican> { 

     @Override 
     protected List<Magican> getMagicables() { 
      ... 
     } 
    } 
  1. दूसरा मामला

संकलन त्रुटि है सामान्य क्योंकि विधि के हस्ताक्षर से <? extends Magicable> का मतलब है कि आप इस बात से परवाह नहीं करते कि आप अपनी सूची के अंदर क्या है n उन तत्वों को मैजिकेबल के रूप में मानें। कॉल करते समय

List<T> list = getMagicables(); 

आप इसे जानने के बिना प्रकार टी का ख्याल रखना चाहते हैं। दूसरे शब्दों में, 3 उपयोग के मामले हैं: टी जादूगर (ठीक है), टी जादूगर है (गलत है क्योंकि getMagicables चुड़ैल की एक सूची वापस कर सकता है) और टी चुड़ैल (गलत भी) है।

  1. मैं क्यों ? extends Magicable बजाय सिर्फ Magicable की सूची

में उपयोग करते हैं List<Magician>List<? extends Magicable> की एक उप-प्रकार नहीं बल्कि List<Magicable> की एक उप-प्रकार है। यह विधियों के मानकों के लिए उपयोगी है।

public void doIt(List<? extends Magicable> list) { 
     // you can't add a Magician here 
    } 

रूप

List<Witch> list = ... 
    doIt(list); 

इस्तेमाल किया जा सकता लेकिन अगर आप

public void doIt(List<Magicable> list) { 
     // you can add a Magician here 
    } 

है आप के रूप में

List<Witch> list = ... 
    doIt(list); // compile error 
1

समस्या के भाग के लिए, आपको हमें यह था, विधि/* 3 */के लिए पर्याप्त है, तो आप अपने कोड के इस भाग के लिए जेनरिक जरूरत नहीं है। लेकिन आपको प्रतिस्थापन का सम्मान करने की आवश्यकता है:

आपको # 1 में त्रुटि मिलती है क्योंकि उप-प्रकार विधि रिटर्न प्रकार की सीमा को सीमित कर रही है: MagicanMagicable है लेकिन इसके विपरीत नहीं है। उप-प्रकार में Magicable के सुपर-प्रकार की अनुमति है। उप-प्रकार विधि को सुपर-टाइप विधि के लिए प्रतिस्थापन योग्य होना चाहिए, जो आपके उदाहरण में नहीं है।

# 2 में त्रुटि वाइल्डकार्ड ?: ? extends Magicable और T extends Magicable की प्रकृति के कारण एक ही प्रकार की आवश्यकता नहीं है। यदि T कक्षा के दायरे में घोषित किया गया है, उदा। कक्षा Magican<T> implements Magicable<T> (बेशक आपके इंटरफ़ेस को इस मामले में टी घोषित करने की आवश्यकता है) T की सभी घटनाएं आपके प्रकार में समान कक्षा का संदर्भ लेंगी।

1
public abstract class AbstractMagicable<T extends Magicable> { 

    abstract List<T> getMagicables1(); 

    abstract List<? extends Magicable> getMagicables2(); 

    abstract List<Magicable> getMagicables3(); 
} 

class MagicableWitch extends AbstractMagicable<Witch> { 

    @Override 
    List<Witch> getMagicables1() { 
     return null; 
    } 

    @Override 
    List<? extends Magicable> getMagicables2() { 
     return getMagicables1(); 
    } 

    @Override 
    List<Magicable> getMagicables3() { 
     return Collections.singletonList(new Witch()); 
    } 
} 

class MagicableMagician extends AbstractMagicable<Magician> { 

    @Override 
    List<Magician> getMagicables1() { 
     return null; 
    } 

    @Override 
    List<? extends Magicable> getMagicables2() { 
     return getMagicables1(); 
    } 

    @Override 
    List<Magicable> getMagicables3() { 
     return Collections.singletonList(new Magician()); 
    } 
} 

1) टी का उपयोग तब किया जाता है जब आप इसका उपयोग करते समय वास्तविक नाम से बदलना चाहते हैं। उदाहरण के लिए class MagicableWitch extends AbstractMagicable<Witch>

यहां Witch ने टी को बदल दिया है और इसलिए abstract List<T> getMagicables1(); को इसकी कंक्रीट कक्षा में List<Witch> getMagicables1() में बदल दिया गया है।

2)? जब आप कक्षा को बदलना चाहते हैं तो इसका उपयोग तब किया जाता है जब रनटाइम पर उपलब्ध होगा।

3) List<Magicable> और List<Witch>Witch implements Magicable भले ही अलग हैं। कार्यान्वयन getMagicables3 में दिखाया गया है।

1

आपके पहले मामले में, अमूर्त विधि को सामान्य प्रकार <T extends Magicable> का उपयोग करने के रूप में घोषित किया जाता है जिसका अर्थ है कि आपकी विधि मैजिकेबल की सूची या किसी भी प्रकार को लागू कर सकती है जो इसे लागू करती है। आपके कार्यान्वयन में, आप कंक्रीट प्रकार मैगिकन लौट रहे हैं जो एक जादुई है। चेतावनी को अक्षम करने के लिए आप चेतावनी को सुरक्षित रूप से अनदेखा कर सकते हैं और @SuppressWarning("unchecked") जोड़ सकते हैं। जागरूक होने की बात यह है कि आपकी कक्षा का विस्तार करने वाली कोई भी कक्षा, केवल मैगिकन की सूचियों को वापस करने के लिए प्रतिबंधित होगी।

दूसरे मामले में, घोषणा List<T> list = getMagicables(); त्रुटि उत्पन्न करती है क्योंकि आपकी विधि List<T> पर वापस नहीं आती है लेकिन List<? extends Magicable' जो एक ही चीज़ नहीं है। जेनेरिक काम करने के तरीके के कारण, जब आप एक रिबाउंड प्रकार घोषित करते हैं जो एक अनबाउंड वाइल्डकार्ड का उपयोग करता है, तो आपके कोड को कॉल करने वाले किसी भी कोड में List<? extends Magicable> या List<?> जैसे आपके मामले में स्वीकार्य मिलान प्रकार होना चाहिए।

तीसरे मामले के संबंध में, आपका अमूर्त तरीका List<Magicable> देता है जबकि आपका कार्यान्वयन List<Magic> देता है। यह counterintuitive प्रतीत हो सकता है, लेकिन आप जावा में जेनेरिक के साथ ऐसा कुछ नहीं कर सकते: List<Magicable> list = ArrayList<Magic>। यह अजीब लग सकता है क्योंकि सरणी आपको Magicable[] magics = new Magican[3]; जैसे कुछ घोषित करने की अनुमति देती हैं। यह एक आम गलतफहमी है क्योंकि जेनिक्स परिवर्तनीय हैं जबकि जेनिक्स परिवर्तनीय हैं। क्या covariant मतलब है कि यदि आपके पास दो वर्ग Super और Sub extends Super, Sub[] is a subtype of Super[] है। जेनेरिकों के लिए, क्योंकि वे परिवर्तनीय हैं, उन दोनों के बीच कोई संबंध नहीं है, List<Sub>List<Super> का उपप्रकार नहीं है।

यदि आप जेनेरिक प्रकार को वापस करना चाहते हैं, तो केवल उसी प्रकार की घोषणा का उपयोग करें जो कक्षाओं में पहली श्रेणी protected <T extends Magicable> List<T> getMagicable() है जो आपकी अमूर्त कक्षा का विस्तार करती है। लौटे हुए कार्ड में वाइल्डकार्ड का उपयोग करना बहुत बुरा विचार है क्योंकि आप अपने वर्ग उपयोगकर्ताओं को अपनी सूची परिवर्तनीय घोषणाओं में वाइल्डकार्ड का उपयोग करने के लिए मजबूर करते हैं।

संबंधित मुद्दे