जावा - बहुरूपता या बंधे प्रकार के मापदंडों का उपयोग करें


17

मान लीजिए मेरे पास यह वर्ग पदानुक्रम है ...

public abstract class Animal {
    public abstract void eat();
    public abstract void talk();
}
class Dog extends Animal {
    @Override
    public void eat() {
    }

    @Override
    public void talk() {
    }
}

class Cat extends Animal {
    @Override
    public void eat() {
    }

    @Override
    public void talk() {
    }
}

और फिर मेरे पास ...।

public static <T extends Animal> void addAnimal(T animal) {
    animal.eat();
    animal.talk();
}

public static void addAnimalPoly(Animal animal) {
    animal.eat();
    animal.talk();
}

बंधे हुए प्रकार के मापदंडों या बहुरूपता का उपयोग करते समय क्या अंतर है?

और एक या दूसरे का उपयोग कब करें?


1
ये दो परिभाषाएँ प्रकार के मापदंडों से बहुत लाभ नहीं उठाती हैं। लेकिन addAnimals(List<Animal>)बिल्लियों की सूची लिखने और जोड़ने का प्रयास करें !
किलिअन फोथ

7
ठीक है, उदाहरण के लिए, यदि आपके प्रत्येक उपरोक्त तरीके से कुछ वापस आ जाएगा, तो जेनेरिक का उपयोग करने वाला टी वापस कर सकता है, जबकि दूसरा केवल पशु को वापस कर सकता है। तो उन तरीकों का उपयोग करने वाले व्यक्ति के लिए, पहले मामले में वह ठीक वही चाहता है जो वह चाहता था: डोड कुत्ता = addAnimal (नया कुत्ता ()); दूसरी विधि के साथ, वह एक कुत्ते को पाने के लिए मजबूर करने के लिए मजबूर होगा: डॉग डी = (डॉग) addAnimalPoly (नया कुत्ता ());
शिवन ड्रैगन

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

जवाबों:


14

ये दो उदाहरण समतुल्य हैं, और वास्तव में एक ही बाईटेकोड के लिए संकलित होंगे।

दो तरीके हैं जो एक बंधे हुए जेनेरिक प्रकार को एक विधि में जोड़ते हैं जैसा कि आपका पहला उदाहरण कुछ भी करेगा।

टाइप पैरामीटर को दूसरे प्रकार से पास करना

ये दो विधि हस्ताक्षर बाइट कोड में समान हैं, लेकिन संकलक प्रकार की सुरक्षा को लागू करता है:

public static <T extends Animal> void addAnimals(Collection<T> animals)

public static void addAnimals(Collection<Animal> animals)

पहले मामले में, केवल Collection(या उपप्रकार) की Animalअनुमति है। दूसरे मामले में, Collectionजेनेरिक प्रकार या उपप्रकार के साथ (या उपप्रकार) Animalकी अनुमति है।

उदाहरण के लिए, पहली विधि में निम्नलिखित की अनुमति है लेकिन दूसरी नहीं:

List<Cat> cats = new ArrayList<Cat>();
cats.add(new Cat());
addAnimals(cats);

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

लौटती हुई वस्तु

दूसरी बार जब यह लौटने वाली वस्तुओं के साथ होता है। चलिए मान लेते हैं कि निम्नलिखित विधि मौजूद है:

public static <T extends Animal> T feed(T animal) {
  animal.eat();
  return animal;
}

आप इसके साथ निम्नलिखित करने में सक्षम होंगे:

Cat c1 = new Cat();
Cat c2 = feed(c1);

हालांकि यह एक विरोधाभासी उदाहरण है, ऐसे मामले हैं जहां यह समझ में आता है। जेनरिक के बिना, विधि को वापस लौटना Animalहोगा और आपको इसे काम करने के लिए टाइप कास्टिंग को जोड़ना होगा (जो कि संकलक वैसे भी बाइट कोड को पर्दे के पीछे जोड़ता है)।


" पहले मामले में, पशु के केवल एक संग्रह (या उपप्रकार) की अनुमति है। दूसरे मामले में, एक सामान्य प्रकार के पशु के साथ एक संग्रह (या उपप्रकार) की अनुमति दी जाती है। " - क्या आप दोगुना करना चाहते हैं- अपने तर्क की जाँच करें?
निधि मोनिका का मुकदमा

3

डाउनकास्टिंग के बजाय जेनरिक का उपयोग करें। "डाउनकास्टिंग" खराब है, सामान्य प्रकार से अधिक विशिष्ट तक जा रहा है:

Animal a = hunter.captureOne();
Cat c = (Cat)a;  // ACK!!!!!! What if it's a Dog? ClassCastException!

... आप भरोसा कर रहे हैं कि aएक बिल्ली है, लेकिन संकलक गारंटी नहीं दे सकता है कि। यह रनटाइम में एक कुत्ता हो सकता है।

यहां आप जेनरिक का उपयोग करेंगे:

public class <A> Hunter() {
    public A captureOne() { ... }
}

अब आप निर्दिष्ट कर सकते हैं कि आप एक बिल्ली का शिकार करना चाहते हैं:

Hunter<Cat> hunterC = new Hunter<Cat>();
Cat c = hunterC.captureOne();

Hunter<Dog> hunterD = new Hunter<Dog>();
Dog d = hunterD.captureOne();

अब संकलक कर सकते हैं गारंटी है कि hunterC केवल बिल्लियों पर कब्जा होगा, और hunterD केवल कब्जा कुत्तों।

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

या, वास्तव में, यदि आपको लगता है कि आपको डाउनकास्ट करना है तो जेनरिक का उपयोग करें।

EDIT: अधिक सामान्य मामला यह है कि आप किस प्रकार के हैंडल को तय करना चाहते हैं। तो प्रकार एक पैरामीटर बन जाते हैं, साथ ही साथ मान भी।

कहो कि मैं अपने चिड़ियाघर वर्ग को बिल्लियों या स्पंज को संभालना चाहता हूं। मेरे पास एक सामान्य सुपर क्लास नहीं है। लेकिन मैं अभी भी उपयोग कर सकता हूं:

public class <T> Zoo() { ... }

Zoo<Sponge> spongeZoo = ...
Zoo<Cat> catZoo = ...

जिस डिग्री को आप लॉक करते हैं, वह इस बात पर निर्भर करता है कि आप क्या करने की कोशिश कर रहे हैं;)


2

यह प्रश्न पुराना-ईश है, लेकिन विचार करने के लिए एक महत्वपूर्ण कारक लगता है कि बहुरूपता बनाम बंधे हुए प्रकार के मापदंडों का उपयोग कब करना है। यह कारक प्रश्न में दिए गए उदाहरण से थोड़ा स्पर्शित हो सकता है लेकिन, मुझे लगता है कि अधिक सामान्य से बहुत प्रासंगिक है "कब पॉलिमॉर्फिज्म बनाम बाउंड प्रकार के मापदंडों का उपयोग करना है?"

टी एल; डॉ

यदि आप कभी भी अपने बेहतर निर्णय के खिलाफ अपने आप को एक उपवर्ग से एक आधार वर्ग तक बढ़ते हुए कोड पाते हैं, तो इसे पॉलीमॉर्फिक तरीके से एक्सेस करने में असमर्थता के कारण, बाध्य प्रकार पैरामीटर एक संभावित समाधान हो सकता है।

पूरा जवाब

बंधे हुए प्रकार के पैरामीटर एक विरासत वाले सदस्य चर के लिए ठोस, गैर-विरासत वाले उपवर्ग विधियों को उजागर कर सकते हैं। बहुरूपता नहीं कर सकता

अपने उदाहरण को विस्तृत करके विस्तृत करें:

public abstract class AnimalOwner<T extends Animal> {
   protected T pet;
   public abstract void rewardPet();
}

// Modify the dog class
class Dog extends Animal {
   // ...
   // This method is not inherited from anywhere!
   public void scratchBelly() {
      System.out.println("Belly: Scratched");
   }
}

class DogOwner extends AnimalOwner<Dog> {
   DogOwner(Dog dog) {
     this.pet = dog;
   }

   @Override
   public void rewardPet()
   {
      // ---- Note this call ----
      pet.scratchBelly();
   }
}

यदि सार वर्ग AnimalOwner को पॉलिमोर्फ़िज्म के लिए ए protected Animal pet;और ऑप्ट करने के लिए परिभाषित किया गया था , तो कंपाइलर pet.scratchBelly();लाइन पर त्रुटि करेगा , आपको बताएगा कि यह विधि पशु के लिए अपरिभाषित है।


1

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

यहां कुछ स्थिति दी गई है जहां आप बंधे हुए प्रकार के मापदंडों का उपयोग करेंगे:

  • संग्रह पैरामीटर

    class Zoo {
    
      private List<Animal> animals;
    
      public void add(Collection<? extends Animal> newAnimals) {
        animals.addAll(newAnimals);
      }
    }

    तो आप कॉल कर सकते हैं

    List<Dog> dogs = ...
    zoo.add(dogs);

    zoo.add(dogs)के बिना संकलित करने में विफल होगा <? extends Animal>, क्योंकि जेनरिक सहसंयोजक नहीं हैं।

  • उपवर्गीकरण

    abstract class Warrior<T extends Weapon> {
    
      public abstract T getWeapon();
    }

    उपवर्ग को प्रदान करने के लिए सीमित कर सकते हैं।

आप यह <T extends A1 & A2 & A3>सुनिश्चित करने के लिए कि सूची में सभी प्रकारों का एक उपप्रकार है, एकाधिक सीमाओं का उपयोग कर सकते हैं ।

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