मैं विधि वापसी प्रकार को सामान्य कैसे बनाऊं?


588

इस उदाहरण पर विचार करें (OOP पुस्तकों में विशिष्ट):

मेरे पास एक Animalवर्ग है, जहां प्रत्येक के Animalकई दोस्त हो सकते हैं।
और उपवर्गों की तरह Dog, Duck, Mouseआदि जो की तरह विशिष्ट व्यवहार को जोड़ने bark(), quack()आदि

यहाँ Animalवर्ग है:

public class Animal {
    private Map<String,Animal> friends = new HashMap<>();

    public void addFriend(String name, Animal animal){
        friends.put(name,animal);
    }

    public Animal callFriend(String name){
        return friends.get(name);
    }
}

और यहाँ बहुत से टाइपकास्टिंग के साथ कुछ कोड स्निपेट है:

Mouse jerry = new Mouse();
jerry.addFriend("spike", new Dog());
jerry.addFriend("quacker", new Duck());

((Dog) jerry.callFriend("spike")).bark();
((Duck) jerry.callFriend("quacker")).quack();

क्या कोई ऐसा तरीका है जिससे मैं टाइपकास्ट से छुटकारा पाने के लिए रिटर्न प्रकार के लिए जेनरिक का उपयोग कर सकता हूं, ताकि मैं कह सकूं

jerry.callFriend("spike").bark();
jerry.callFriend("quacker").quack();

यहां कुछ प्रारंभिक कोड रिटर्न प्रकार के साथ एक पैरामीटर के रूप में विधि से अवगत कराया जाता है जो कभी उपयोग नहीं किया जाता है।

public<T extends Animal> T callFriend(String name, T unusedTypeObj){
    return (T)friends.get(name);        
}

क्या अतिरिक्त पैरामीटर का उपयोग किए बिना रनटाइम पर रिटर्न प्रकार का पता लगाने का एक तरीका है instanceof? या कम से कम डमी उदाहरण के बजाय प्रकार के एक वर्ग को पास करके।
मैं समझता हूं कि जेनरिक संकलन समय-जाँच के लिए हैं, लेकिन क्या इसके लिए कोई समाधान है?

जवाबों:


902

आप callFriendइस तरह परिभाषित कर सकते हैं :

public <T extends Animal> T callFriend(String name, Class<T> type) {
    return type.cast(friends.get(name));
}

तो इसे इस तरह से कॉल करें:

jerry.callFriend("spike", Dog.class).bark();
jerry.callFriend("quacker", Duck.class).quack();

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


29
... लेकिन अभी भी कॉलफ्रीड () कॉल के मापदंडों के बीच कोई संकलन समय जाँच नहीं है।
डेविड श्मिट

2
यह अब तक का सबसे अच्छा जवाब है - लेकिन आपको उसी तरह से AddFriend को बदलना चाहिए। इससे आपको बग लिखना मुश्किल हो जाता है क्योंकि आपको दोनों जगहों पर उस वर्ग शाब्दिक की आवश्यकता होती है।
क्रेग पी। मोटलिन

@ जैदर, बिल्कुल वैसा ही नहीं बल्कि यह काम करेगा: // एनिमल क्लास पब्लिक टी कॉलफ्रेंड <टी> (स्ट्रिंग नाम) जहां टी: एनिमल {रिटर्न फ्रेंड्स [नाम] टी के रूप में; } // कॉलिंग क्लास जेरी।कॉलफ्रेंड <डॉग> ("स्पाइक")। बार्क (); jerry.CallFriend <बतख> ( "क्वैकर") नीम हकीम ()।;
नेस्टर लेडन

124

संकलक यह नहीं जान सकता कि किस प्रकार jerry.callFriend("spike")की वापसी होगी। इसके अलावा, आपका कार्यान्वयन किसी भी अतिरिक्त प्रकार की सुरक्षा के बिना केवल विधि में कलाकारों को छुपाता है। इस पर विचार करो:

jerry.addFriend("quaker", new Duck());
jerry.callFriend("quaker", /* unused */ new Dog()); // dies with illegal cast

इस विशिष्ट मामले में, एक अमूर्त talk()विधि बनाने और उपवर्गों में उचित रूप से इसे ओवरराइड करने से आप बेहतर सेवा कर सकते हैं:

Mouse jerry = new Mouse();
jerry.addFriend("spike", new Dog());
jerry.addFriend("quacker", new Duck());

jerry.callFriend("spike").talk();
jerry.callFriend("quacker").talk();

10
जबकि mmyers विधि काम कर सकती है, मुझे लगता है कि यह विधि बेहतर OO प्रोग्रामिंग है और भविष्य में आपको कुछ परेशानी से बचाएगी।
जेम्स मैकमोहन

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

लेकिन मूल प्रश्न प्रकार सुरक्षा के बारे में नहीं पूछ रहा है। जिस तरह से मैंने इसे प्रश्नकर्ता को पढ़ा है वह सिर्फ यह जानना चाहता है कि क्या कास्ट करने से बचने के लिए जेनरिक का लाभ उठाने का एक तरीका है।
लाज

2
@ लाज़: हाँ, मूल प्रश्न - जैसा कि सामने आया - प्रकार की सुरक्षा के बारे में नहीं है। यह इस तथ्य को नहीं बदलता है कि इसे लागू करने का एक सुरक्षित तरीका है, वर्ग की विफलताओं को समाप्त करना। यह भी देखें weblogs.asp.net/alex_papadimoulis/archive/2005/05/25/...
डेविड श्मिट

3
मैं इसके बारे में असहमत नहीं हूं, लेकिन हम जावा और इसके सभी डिजाइन निर्णयों / झटकों से निपट रहे हैं। मैं इस सवाल को केवल जावा जेनरिक में संभव समझने की कोशिश के रूप में देख रहा हूं, न कि xyproblem ( meta.stackexchange.com/questions/66377/what-is-the-xy-problem ) के रूप में फिर से इंजीनियर होने की जरूरत है। किसी भी पैटर्न या दृष्टिकोण की तरह, ऐसे समय होते हैं जब मैंने जो कोड प्रदान किया है वह उचित और समय है जब कुछ पूरी तरह से अलग होता है (जैसे कि आपने इस उत्तर में क्या सुझाव दिया है) की आवश्यकता है।
लाज जूल

114

आप इसे इस तरह से लागू कर सकते हैं:

@SuppressWarnings("unchecked")
public <T extends Animal> T callFriend(String name) {
    return (T)friends.get(name);
}

(हां, यह कानूनी कोड है; जावा जेनरिक देखें : सामान्य प्रकार केवल रिटर्न प्रकार के रूप में परिभाषित किया गया है ।)

कॉल करने वाले से रिटर्न प्रकार का अनुमान लगाया जाएगा। हालाँकि, @SuppressWarningsएनोटेशन पर ध्यान दें : जो आपको बताता है कि यह कोड प्रकार नहीं है । आपको इसे स्वयं सत्यापित करना होगा, या आप ClassCastExceptionsरनटाइम पर प्राप्त कर सकते हैं ।

दुर्भाग्यवश, जिस तरह से आप इसका उपयोग कर रहे हैं (बिना अस्थायी मान के रिटर्न वैल्यू असाइन किए बिना), कंपाइलर को खुश करने का एकमात्र तरीका इसे इस तरह से कॉल करना है:

jerry.<Dog>callFriend("spike").bark();

हालांकि यह कास्टिंग से थोड़ा अच्छा हो सकता है, आप शायद Animalकक्षा को एक सार talk()पद्धति देने से बेहतर हैं , जैसा कि डेविड शमिट ने कहा था।


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

यह पूरी तरह से काम करता है जब विधि कॉलिंग कर रहा है!
हार्टमुट पी।

मैं वास्तव में इस वाक्यविन्यास को पसंद करता हूं। मुझे लगता है कि C # ऐसा है jerry.CallFriend<Dog>(...जो मुझे लगता है कि बेहतर है।
औरो सेप

दिलचस्प है कि जेआरई का अपना java.util.Collections.emptyList()कार्य बिल्कुल इस तरह से लागू किया गया है, और इसका जेडाडॉक खुद को टाइपसेफ़ के रूप में विज्ञापित करता है।
टीआई स्ट्रगा

31

यह सवाल प्रभावी जावा में आइटम 29 के समान है - "टाइप विषम कंटेनरों पर विचार करें।" लाज का जवाब बलोच के समाधान के सबसे करीब है। हालाँकि, सुरक्षा के लिए क्लास लिटल का इस्तेमाल करना चाहिए। हस्ताक्षर बनेंगे:

public <T extends Animal> void addFriend(String name, Class<T> type, T animal);
public <T extends Animal> T callFriend(String name, Class<T> type);

दोनों विधियों के अंदर आपको जांचना चाहिए कि पैरामीटर समझदार हैं। अधिक जानकारी के लिए प्रभावी जावा और कक्षा javadoc देखें ।


17

इसके अतिरिक्त आप किसी दिए गए प्रकार में मान वापस करने की विधि पूछ सकते हैं

<T> T methodName(Class<T> var);

ओरेकल जावा प्रलेखन में यहाँ और अधिक उदाहरण हैं


17

यहाँ सरल संस्करण है:

public <T> T callFriend(String name) {
    return (T) friends.get(name); //Casting to T not needed in this case but its a good practice to do
}

पूरी तरह से काम कोड:

    public class Test {
        public static class Animal {
            private Map<String,Animal> friends = new HashMap<>();

            public void addFriend(String name, Animal animal){
                friends.put(name,animal);
            }

            public <T> T callFriend(String name){
                return (T) friends.get(name);
            }
        }

        public static class Dog extends Animal {

            public void bark() {
                System.out.println("i am dog");
            }
        }

        public static class Duck extends Animal {

            public void quack() {
                System.out.println("i am duck");
            }
        }

        public static void main(String [] args) {
            Animal animals = new Animal();
            animals.addFriend("dog", new Dog());
            animals.addFriend("duck", new Duck());

            Dog dog = animals.callFriend("dog");
            dog.bark();

            Duck duck = animals.callFriend("duck");
            duck.quack();

        }
    }

1
क्या करता है Casting to T not needed in this case but it's a good practice to do। मेरा मतलब है कि अगर रनटाइम के दौरान सही तरीके से देखभाल की जाए तो "अच्छा अभ्यास" का क्या मतलब है?
फरीद

मेरा कहने का मतलब है कि स्पष्ट कास्टिंग (T) आवश्यक नहीं है क्योंकि वापसी की घोषणा <टी> विधि घोषणा पर पर्याप्त पर्याप्त होनी चाहिए
वेबजॉकी

9

जैसा कि आपने कहा था कि कक्षा उत्तीर्ण करना ठीक होगा, आप इसे लिख सकते हैं:

public <T extends Animal> T callFriend(String name, Class<T> clazz) {
   return (T) friends.get(name);
}

और फिर इसे इस तरह उपयोग करें:

jerry.callFriend("spike", Dog.class).bark();
jerry.callFriend("quacker", Duck.class).quack();

सही नहीं है, लेकिन यह बहुत ज्यादा है जहाँ तक आप जावा जेनरिक के साथ मिलते हैं। सुपर टाइप टोकन का उपयोग करके टाइपसेफ हेटरोजेनस कंटेनर (THC) को लागू करने का एक तरीका है , लेकिन इसकी अपनी समस्याएं फिर से हैं।


मुझे खेद है लेकिन यह वही उत्तर है जो लाज़ के पास है, इसलिए आप या तो उसे कॉपी कर रहे हैं या वह आपको कॉपी कर रहा है।
जेम्स मैकमोहन

प्रकार पास करने के लिए एक साफ तरीका है। लेकिन फिर भी यह Schmitt की तरह सुरक्षित नहीं है। मैं अभी भी एक अलग वर्ग पास कर सकता हूं और टाइपकास्टिंग बम होगा। एमएमआर 2 के प्रकार को रिटर्न टाइप में सेट करने का उत्तर बेहतर लगता है
सतीश

4
यदि आप पोस्टिंग समय की जाँच करते हैं, तो निमो, आप देखेंगे कि हमने उन्हें उसी क्षण बहुत सटीक रूप से पोस्ट किया है। इसके अलावा, वे केवल एक ही नहीं हैं, केवल दो लाइनें।
फेबियन स्टैग

@ फैबियन मैंने एक समान उत्तर पोस्ट किया, लेकिन बलोच की स्लाइड्स और इफेक्टिव जावा में प्रकाशित होने के बीच एक महत्वपूर्ण अंतर है। वह TypeRef <T> के बजाय कक्षा <T> का उपयोग करता है। लेकिन यह अभी भी एक महान जवाब है।
क्रेग पी। मोटलिन

8

सुपर टाइप टोकन के समान विचार के आधार पर, आप एक स्ट्रिंग के बजाय उपयोग करने के लिए एक टाइप आईडी बना सकते हैं:

public abstract class TypedID<T extends Animal> {
  public final Type type;
  public final String id;

  protected TypedID(String id) {
    this.id = id;
    Type superclass = getClass().getGenericSuperclass();
    if (superclass instanceof Class) {
      throw new RuntimeException("Missing type parameter.");
    }
    this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
  }
}

लेकिन मुझे लगता है कि यह उद्देश्य को हरा सकता है, क्योंकि अब आपको प्रत्येक स्ट्रिंग के लिए नई आईडी ऑब्जेक्ट बनाने और उन पर पकड़ बनाने की आवश्यकता है (या उन्हें सही प्रकार की जानकारी के साथ फिर से संगठित करें)।

Mouse jerry = new Mouse();
TypedID<Dog> spike = new TypedID<Dog>("spike") {};
TypedID<Duck> quacker = new TypedID<Duck>("quacker") {};

jerry.addFriend(spike, new Dog());
jerry.addFriend(quacker, new Duck());

लेकिन अब आप वर्ग का उपयोग उस तरह से कर सकते हैं जैसे आप मूल रूप से चाहते थे, बिना कलाकारों के।

jerry.callFriend(spike).bark();
jerry.callFriend(quacker).quack();

यह सिर्फ आईडी के अंदर टाइप पैरामीटर छिपा रहा है, हालांकि इसका मतलब है कि आप पहचानकर्ता से बाद में टाइप कर सकते हैं यदि आप चाहें।

यदि आप एक आईडी के दो समान उदाहरणों की तुलना करने में सक्षम होना चाहते हैं, तो आपको टाइपेडिड की तुलना और हैशिंग विधियों को भी लागू करना होगा।


8

"क्या कोई ऐसा तरीका है जो इंस्टाफॉरेक्ट का उपयोग किए बिना अतिरिक्त पैरामीटर के बिना रनटाइम पर रिटर्न टाइप का पता लगा सकता है?"

एक वैकल्पिक समाधान के रूप में आप विज़िटर पैटर्न का इस तरह उपयोग कर सकते हैं । पशु को अमूर्त बनाएं और इसे लागू करने योग्य बनाएं:

abstract public class Animal implements Visitable {
  private Map<String,Animal> friends = new HashMap<String,Animal>();

  public void addFriend(String name, Animal animal){
      friends.put(name,animal);
  }

  public Animal callFriend(String name){
      return friends.get(name);
  }
}

दृश्यमान का अर्थ है कि एक पशु कार्यान्वयन एक आगंतुक को स्वीकार करने के लिए तैयार है:

public interface Visitable {
    void accept(Visitor v);
}

और एक आगंतुक कार्यान्वयन एक जानवर के सभी उपवर्गों का दौरा करने में सक्षम है:

public interface Visitor {
    void visit(Dog d);
    void visit(Duck d);
    void visit(Mouse m);
}

उदाहरण के लिए एक कुत्ता कार्यान्वयन तो इस तरह दिखेगा:

public class Dog extends Animal {
    public void bark() {}

    @Override
    public void accept(Visitor v) { v.visit(this); }
}

यहाँ चाल यह है कि जैसा कि डॉग जानता है कि वह किस प्रकार का है, यह पैरामीटर के रूप में "यह" पास करके विज़िटर v की प्रासंगिक अतिभारित यात्रा विधि को ट्रिगर कर सकता है। अन्य उपवर्ग स्वीकार () को ठीक उसी तरह लागू करेंगे।

वर्ग जो उपवर्ग विशिष्ट तरीकों को कॉल करना चाहता है, उसे इस तरह विज़िटर इंटरफ़ेस लागू करना होगा:

public class Example implements Visitor {

    public void main() {
        Mouse jerry = new Mouse();
        jerry.addFriend("spike", new Dog());
        jerry.addFriend("quacker", new Duck());

        // Used to be: ((Dog) jerry.callFriend("spike")).bark();
        jerry.callFriend("spike").accept(this);

        // Used to be: ((Duck) jerry.callFriend("quacker")).quack();
        jerry.callFriend("quacker").accept(this);
    }

    // This would fire on callFriend("spike").accept(this)
    @Override
    public void visit(Dog d) { d.bark(); }

    // This would fire on callFriend("quacker").accept(this)
    @Override
    public void visit(Duck d) { d.quack(); }

    @Override
    public void visit(Mouse m) { m.squeak(); }
}

मुझे पता है कि आपके लिए सौदेबाजी की तुलना में यह बहुत अधिक इंटरफेस और तरीके हैं, लेकिन यह प्रत्येक विशिष्ट उपप्रकार पर सटीक शून्य इंस्टाफॉफ चेक और शून्य प्रकार के कास्ट के साथ एक मानक तरीका है। और यह सब एक मानक भाषा में किया जाता है अज्ञेय फैशन तो यह सिर्फ जावा के लिए नहीं है, लेकिन किसी भी ओओ भाषा को उसी तरह काम करना चाहिए।


6

संभव नहीं। मानचित्र को यह कैसे पता चलता है कि पशु को कौन सा उपवर्ग मिलने वाला है, जिसे केवल एक स्ट्रिंग कुंजी दी गई है?

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


5

मैंने एक लेख लिखा है जिसमें अवधारणा, समर्थन कक्षाएं और एक परीक्षण वर्ग शामिल है जो दर्शाता है कि रन टाइम के दौरान आपकी कक्षाओं द्वारा सुपर टाइप टोकन कैसे प्राप्त किए जा सकते हैं। संक्षेप में, यह आपको कॉलर द्वारा पारित वास्तविक जेनेरिक मापदंडों के आधार पर वैकल्पिक कार्यान्वयन को सौंपने की अनुमति देता है। उदाहरण:

  • TimeSeries<Double> एक निजी आंतरिक वर्ग को सौंपता है जो उपयोग करता है double[]
  • TimeSeries<OHLC> एक निजी आंतरिक वर्ग को सौंपता है जो उपयोग करता है ArrayList<OHLC>

देखें: जेनेरिक मापदंडों को पुनः प्राप्त करने के लिए TypeTokens का उपयोग करना

धन्यवाद

रिचर्ड गोम्स - ब्लॉग


वास्तव में, अपनी अंतर्दृष्टि साझा करने के लिए धन्यवाद, आपका लेख वास्तव में यह सब समझाता है!
यान-गेल गुहेन्यूक

3

यहां बहुत सारे शानदार उत्तर हैं, लेकिन यह एक दृष्टिकोण है जो मैंने एक ऐपियम परीक्षण के लिए लिया था, जहां एक तत्व पर अभिनय करने के परिणामस्वरूप उपयोगकर्ता की सेटिंग्स के आधार पर विभिन्न एप्लिकेशन राज्यों में जा सकते हैं। हालांकि यह ओपी के उदाहरण के सम्मेलनों का पालन नहीं करता है, मुझे आशा है कि यह किसी की मदद करता है।

public <T extends MobilePage> T tapSignInButton(Class<T> type) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    //signInButton.click();
    return type.getConstructor(AppiumDriver.class).newInstance(appiumDriver);
}
  • MobilePage सुपर क्लास है जिसका प्रकार अर्थ है कि आप इसके किसी भी बच्चे (डु) का उपयोग कर सकते हैं
  • type.getConstructor (Param.class, आदि) आपको प्रकार के निर्माता के साथ बातचीत करने की अनुमति देता है। यह कंस्ट्रक्टर सभी अपेक्षित वर्गों के बीच समान होना चाहिए।
  • newInstance एक घोषित चर लेता है जिसे आप नए ऑब्जेक्ट्स कंस्ट्रक्टर के पास भेजना चाहते हैं

यदि आप त्रुटियों को फेंकना नहीं चाहते हैं तो आप उन्हें इस तरह पकड़ सकते हैं:

public <T extends MobilePage> T tapSignInButton(Class<T> type) {
    // signInButton.click();
    T returnValue = null;
    try {
       returnValue = type.getConstructor(AppiumDriver.class).newInstance(appiumDriver);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return returnValue;
}

मेरी समझ से, यह जेनरिक का उपयोग करने का सबसे अच्छा और सबसे सुंदर तरीका है।
cbaldan

2

वास्तव में नहीं, क्योंकि जैसा कि आप कहते हैं, संकलक केवल यह जानता है कि कॉलफ्रीड () एक जानवर, कुत्ते या बतख नहीं लौटा रहा है।

क्या आप एनीमल मेकओनिज़ () मेथड को नहीं जोड़ सकते हैं जो कि जानवरों को अपने उपवर्गों द्वारा छाल या क्वैक के रूप में लागू किया जाएगा?


1
क्या होगा अगर जानवरों के पास कई तरीके हैं जो एक सामान्य क्रिया के तहत भी नहीं आते हैं जो अमूर्त हो सकते हैं? मुझे विभिन्न कार्यों के साथ उपवर्गों के बीच संचार के लिए इसकी आवश्यकता है जहां टाइप करने के साथ im ठीक है, उदाहरण के लिए नहीं।
सतीश

2
आपने वास्तव में सिर्फ अपने ही प्रश्न का उत्तर दिया है - यदि किसी जानवर की कोई अनोखी क्रिया है, तो आपको उस विशिष्ट जानवर को डालना होगा। यदि एक जानवर के पास एक ऐसी क्रिया है जिसे अन्य जानवरों के साथ समूहीकृत किया जा सकता है, तो आप आधार वर्ग में एक सार या आभासी पद्धति को परिभाषित कर सकते हैं और उसका उपयोग कर सकते हैं।
मैट जॉर्डन

2

आप यहां जो देख रहे हैं वह अमूर्त है। इंटरफेस के खिलाफ कोड अधिक है और आपको कम कास्टिंग करनी चाहिए।

नीचे उदाहरण C # में है, लेकिन अवधारणा समान है।

using System;
using System.Collections.Generic;
using System.Reflection;

namespace GenericsTest
{
class MainClass
{
    public static void Main (string[] args)
    {
        _HasFriends jerry = new Mouse();
        jerry.AddFriend("spike", new Dog());
        jerry.AddFriend("quacker", new Duck());

        jerry.CallFriend<_Animal>("spike").Speak();
        jerry.CallFriend<_Animal>("quacker").Speak();
    }
}

interface _HasFriends
{
    void AddFriend(string name, _Animal animal);

    T CallFriend<T>(string name) where T : _Animal;
}

interface _Animal
{
    void Speak();
}

abstract class AnimalBase : _Animal, _HasFriends
{
    private Dictionary<string, _Animal> friends = new Dictionary<string, _Animal>();


    public abstract void Speak();

    public void AddFriend(string name, _Animal animal)
    {
        friends.Add(name, animal);
    }   

    public T CallFriend<T>(string name) where T : _Animal
    {
        return (T) friends[name];
    }
}

class Mouse : AnimalBase
{
    public override void Speak() { Squeek(); }

    private void Squeek()
    {
        Console.WriteLine ("Squeek! Squeek!");
    }
}

class Dog : AnimalBase
{
    public override void Speak() { Bark(); }

    private void Bark()
    {
        Console.WriteLine ("Woof!");
    }
}

class Duck : AnimalBase
{
    public override void Speak() { Quack(); }

    private void Quack()
    {
        Console.WriteLine ("Quack! Quack!");
    }
}
}

यह सवाल कोडिंग नहीं अवधारणा के साथ करना है।

2

मैंने अपनी लीबी कन्ट्रोकेटर में निम्न कार्य किया:

public class Actor<SELF extends Actor> {
    public SELF self() { return (SELF)_self; }
}

उपवर्गीकरण:

public class MyHttpAppSession extends Actor<MyHttpAppSession> {
   ...
}

कम से कम यह वर्तमान वर्ग के अंदर काम करता है और जब एक मजबूत टाइपिंग संदर्भ होता है। एकाधिक विरासत काम करता है, लेकिन वास्तव में मुश्किल हो जाता है :)


1

मुझे पता है कि यह एक पूरी तरह से अलग बात है जो एक ने पूछा। इसे हल करने का एक और तरीका प्रतिबिंब होगा। मेरा मतलब है, यह जेनरिक से लाभ नहीं उठाता है, लेकिन यह आपको किसी भी तरह का ध्यान रखे बिना, व्यवहार को आप प्रदर्शन करना चाहते हैं (कुत्ते की छाल बनाना, बत्तख का बच्चा बनाना, आदि)।

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

abstract class AnimalExample {
    private Map<String,Class<?>> friends = new HashMap<String,Class<?>>();
    private Map<String,Object> theFriends = new HashMap<String,Object>();

    public void addFriend(String name, Object friend){
        friends.put(name,friend.getClass());
        theFriends.put(name, friend);
    }

    public void makeMyFriendSpeak(String name){
        try {
            friends.get(name).getMethod("speak").invoke(theFriends.get(name));
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    } 

    public abstract void speak ();
};

class Dog extends Animal {
    public void speak () {
        System.out.println("woof!");
    }
}

class Duck extends Animal {
    public void speak () {
        System.out.println("quack!");
    }
}

class Cat extends Animal {
    public void speak () {
        System.out.println("miauu!");
    }
}

public class AnimalExample {

    public static void main (String [] args) {

        Cat felix = new Cat ();
        felix.addFriend("Spike", new Dog());
        felix.addFriend("Donald", new Duck());
        felix.makeMyFriendSpeak("Spike");
        felix.makeMyFriendSpeak("Donald");

    }

}

1

किस बारे में

public class Animal {
private Map<String,<T extends Animal>> friends = new HashMap<String,<T extends Animal>>();

public <T extends Animal> void addFriend(String name, T animal){
    friends.put(name,animal);
}

public <T extends Animal> T callFriend(String name){
    return friends.get(name);
}

}


0

एक और दृष्टिकोण है, जब आप किसी विधि को ओवरराइड करते हैं तो आप रिटर्न प्रकार को संकीर्ण कर सकते हैं। प्रत्येक उपवर्ग में आपको उस उपवर्ग को वापस करने के लिए कॉलफ्रेंड को ओवरराइड करना होगा। लागत कॉलफ्रेंड की कई घोषणाएं होगी, लेकिन आप आम भागों को आंतरिक रूप से कहे गए तरीके से अलग कर सकते हैं। यह ऊपर वर्णित समाधानों की तुलना में मेरे लिए बहुत सरल है, और वापसी प्रकार निर्धारित करने के लिए अतिरिक्त तर्क की आवश्यकता नहीं है।


मुझे यकीन नहीं है कि आप "वापसी प्रकार को संकीर्ण" से क्या मतलब है। Afaik, Java और अधिकांश टाइप की गई भाषाएं रिटर्न प्रकार के आधार पर तरीकों या कार्यों को अधिभार नहीं देती हैं। उदाहरण के लिए , संकलक दृष्टिकोण से public int getValue(String name){}अप्रभेद्य है public boolean getValue(String name){}। आपको या तो पैरामीटर प्रकार को बदलने या अधिभार के लिए पैरामीटर को जोड़ने / हटाने की आवश्यकता होगी। हो सकता है कि मैं आपको सिर्फ गलत समझ रहा हूं ...
द वन ट्रू कोल्टर

जावा में आप एक उपवर्ग में एक विधि को ओवरराइड कर सकते हैं और अधिक "संकीर्ण" (यानी अधिक विशिष्ट) रिटर्न प्रकार निर्दिष्ट कर सकते हैं। Stackoverflow.com/questions/14694852/… देखें ।
FeralWhippet

-3
public <X,Y> X nextRow(Y cursor) {
    return (X) getRow(cursor);
}

private <T> Person getRow(T cursor) {
    Cursor c = (Cursor) cursor;
    Person s = null;
    if (!c.moveToNext()) {
        c.close();
    } else {
        String id = c.getString(c.getColumnIndex("id"));
        String name = c.getString(c.getColumnIndex("name"));
        s = new Person();
        s.setId(id);
        s.setName(name);
    }
    return s;
}

आप किसी भी प्रकार को वापस कर सकते हैं और सीधे प्राप्त कर सकते हैं। टाइपकास्ट करने की जरूरत नहीं है।

Person p = nextRow(cursor); // cursor is real database cursor.

यह सबसे अच्छा है यदि आप वास्तविक कर्सर के बजाय किसी अन्य प्रकार के रिकॉर्ड को अनुकूलित करना चाहते हैं।


2
आप कहते हैं कि "टाइपकास्ट करने की कोई आवश्यकता नहीं है", लेकिन स्पष्ट रूप से इसमें टाइपकास्टिंग शामिल है: (Cursor) cursorउदाहरण के लिए।
सामी Laine

यह जेनरिक का पूरी तरह से अनुचित उपयोग है। इस कोड को cursorएक होने की आवश्यकता है Cursor, और हमेशा एक Person(या अशक्त) वापस आ जाएगी । जेनरिक का उपयोग इन बाधाओं की जाँच को हटा देता है, जिससे कोड असुरक्षित हो जाता है।
एंडी टर्नर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.