java - लैब्राडोर कुत्ता कीमत




सूची<कुत्ता> सूची<Animal> का एक उप-वर्ग है? जावा जेनेरिक क्यों नहीं हैं polymorphic? (10)

मैं जावा जेनिक्स विरासत / बहुरूपता को कैसे संभालने के बारे में थोड़ा उलझन में हूं।

निम्नलिखित पदानुक्रम मानें -

पशु (अभिभावक)

कुत्ता - बिल्ली (बच्चे)

तो मान लीजिए मेरे पास एक विधि है doSomething(List<Animal> animals) । विरासत और बहुरूपता के सभी नियमों से, मुझे लगता है कि एक List<Dog> एक List<Animal> और एक List<Cat> एक List<Animal> - और इसलिए कोई भी इस विधि को पारित किया जा सकता है। ऐसा नहीं। अगर मैं इस व्यवहार को हासिल करना चाहता हूं, तो मुझे कुछ कहकर पशु के किसी भी उप-समूह की सूची को स्वीकार करने के तरीके को स्पष्ट रूप से बताना होगा doSomething(List<? extends Animal> animals)

मैं समझता हूं कि यह जावा का व्यवहार है। मेरा सवाल है क्यों ? बहुरूपता आम तौर पर क्यों निहित है, लेकिन जब जेनेरिक की बात आती है तो इसे निर्दिष्ट किया जाना चाहिए?


असल में आप जो चाहते हैं उसे प्राप्त करने के लिए एक इंटरफ़ेस का उपयोग कर सकते हैं।

public interface Animal {
    String getName();
    String getVoice();
}
public class Dog implements Animal{
    @Override 
    String getName(){return "Dog";}
    @Override
    String getVoice(){return "woof!";}

}

फिर आप संग्रह का उपयोग कर सकते हैं

List <Animal> animalGroup = new ArrayList<Animal>();
animalGroup.add(new Dog());

आइए जावाएसई tutorial से उदाहरण लें

public abstract class Shape {
    public abstract void draw(Canvas c);
}

public class Circle extends Shape {
    private int x, y, radius;
    public void draw(Canvas c) {
        ...
    }
}

public class Rectangle extends Shape {
    private int x, y, width, height;
    public void draw(Canvas c) {
        ...
    }
}

तो क्यों कुत्तों (मंडलियों) की एक सूची को पूरी तरह से नहीं माना जाना चाहिए जानवरों (आकार) की एक सूची इस स्थिति के कारण है:

// drawAll method call
drawAll(circleList);


public void drawAll(List<Shape> shapes) {
   shapes.add(new Rectangle());    
}

तो जावा "आर्किटेक्ट्स" में 2 विकल्प थे जो इस समस्या को हल करते हैं:

  1. इस बात पर विचार न करें कि एक उप प्रकार निश्चित रूप से यह सुपरर्ट टाइप है, और एक संकलन त्रुटि दें, जैसा कि अब होता है

  2. उपप्रकार को यह सुपरटेप होने पर विचार करें और "ऐड" विधि को संकलित करने पर प्रतिबंधित करें (इसलिए ड्रॉ में सभी विधि, यदि मंडलियों की सूची, आकार का उप प्रकार, पारित किया जाएगा, तो संकलक को पता होना चाहिए और संकलन त्रुटि के साथ आपको प्रतिबंधित करना चाहिए उस)।

स्पष्ट कारणों से, जिसने पहली बार चुना।


एक बिंदु मुझे लगता है कि other answers उल्लेख करने के लिए जोड़ा जाना चाहिए कि जबकि

List<Dog> जावा में एक List<Animal>

यह भी सच है कि

कुत्तों की एक सूची है- अंग्रेजी में जानवरों की एक सूची (अच्छी तरह से, उचित व्याख्या के तहत)

जिस तरह ओपी का अंतर्ज्ञान काम करता है - जो पूरी तरह से पाठ्यक्रम के लिए मान्य है - बाद की सजा है। हालांकि, अगर हम इस अंतर्ज्ञान को लागू करते हैं तो हमें ऐसी भाषा मिलती है जो जावा-एस्क्यू अपने प्रकार की प्रणाली में नहीं है: मान लीजिए कि हमारी भाषा कुत्तों की सूची में एक बिल्ली जोड़ने की अनुमति देती है। इसका क्या मतलब होगा? इसका मतलब यह होगा कि सूची कुत्तों की एक सूची बन जाती है, और केवल जानवरों की एक सूची बनी हुई है। और स्तनधारियों की एक सूची, और चौकोर की एक सूची।

इसे एक और तरीका रखने के लिए: जावा में एक List<Dog> का अर्थ अंग्रेजी में "कुत्तों की एक सूची" नहीं है, इसका अर्थ है "एक सूची जिसमें कुत्ते हो सकते हैं, और कुछ और नहीं"।

अधिक आम तौर पर, ओपी की अंतर्ज्ञान खुद को ऐसी भाषा की तरफ उधार देती है जिसमें ऑब्जेक्ट्स पर ऑपरेशन उनके प्रकार को बदल सकते हैं , या बल्कि, ऑब्जेक्ट का प्रकार इसके मान का एक (गतिशील) फ़ंक्शन है।


जो आप खोज रहे हैं उसे कोविरिएंट प्रकार पैरामीटर कहा जाता है। समस्या यह है कि वे सामान्य मामले में विशेष रूप से म्यूटेबल सूचियों के लिए टाइप-सुरक्षित नहीं हैं। मान लें कि आपके पास एक List<Dog> , और इसे List<Animal> रूप में कार्य करने की अनुमति है। क्या होता है जब आप इस List<Animal> बिल्ली जोड़ने की कोशिश करते हैं List<Animal> जो वास्तव में एक List<Dog> ? स्वचालित रूप से प्रकार पैरामीटर को covariant होने की इजाजत देता है इसलिए टाइप सिस्टम को तोड़ देता है।

टाइप पैरामीटर को कॉन्वेंट के रूप में निर्दिष्ट करने के लिए सिंटैक्स जोड़ने के लिए उपयोगी होगा, जो इससे बचाता है ? extends Foo विधि घोषणाओं में ? extends Foo , लेकिन यह अतिरिक्त जटिलता जोड़ता है।


मैं कहूंगा कि जेनेरिक का पूरा बिंदु यह है कि यह इसकी अनुमति नहीं देता है। सरणी के साथ स्थिति पर विचार करें, जो उस प्रकार की कॉन्वर्सिस की अनुमति देता है:

  Object[] objects = new String[10];
  objects[0] = Boolean.FALSE;

वह कोड ठीक संकलित करता है, लेकिन रनटाइम त्रुटि फेंकता है ( java.lang.ArrayStoreException: java.lang.Boolean दूसरी पंक्ति में)। यह टाइपएफ़ नहीं है। जेनेरिक का बिंदु संकलन समय प्रकार की सुरक्षा को जोड़ना है, अन्यथा आप जेनरिक के बिना एक सादे वर्ग के साथ रह सकते हैं।

अब ऐसे समय हैं जहां आपको अधिक लचीला होने की आवश्यकता है और यही वह है ? super Class ? super Class और ? extends Class ? extends Class लिए ? extends Class हैं। पूर्व तब होता है जब आपको एक प्रकार के Collection (उदाहरण के लिए) में डालने की आवश्यकता होती है, और बाद वाला तब होता है जब आपको इसे एक प्रकार से सुरक्षित तरीके से पढ़ने की आवश्यकता होती है। लेकिन एक ही समय में दोनों को करने का एकमात्र तरीका एक विशिष्ट प्रकार होना है।


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

(List<Animal>) (List<?>) dogs

यह तब उपयोगी होता है जब आप किसी कन्स्ट्रक्टर में सूची को पास करना चाहते हैं या उस पर पुनरावृत्ति करना चाहते हैं


समस्या अच्छी तरह से पहचाना गया है। लेकिन एक समाधान है; कुछ सामान्य बनाओ:

<T extends Animal> void doSomething<List<T> animals) {
}

अब आप सूची <कुत्ते> या सूची <बिल्ली> या सूची <Animal> के साथ कुछ भी कॉल कर सकते हैं।


समस्या को समझने के लिए सरणी की तुलना करना उपयोगी है।

List<Dog> List<Animal> का उप-वर्ग नहीं है
लेकिन Dog[] Animal[] उप-वर्ग है Animal[]

Arrays reifiable और covariant हैं
reifiable मतलब है कि उनकी प्रकार की जानकारी रनटाइम पर पूरी तरह से उपलब्ध है।
इसलिए सरणी रनटाइम प्रकार की सुरक्षा प्रदान करती है लेकिन संकलित-समय प्रकार की सुरक्षा नहीं।

    // All compiles but throws ArrayStoreException at runtime at last line
    Dog[] dogs = new Dog[10];
    Animal[] animals = dogs; // compiles
    animals[0] = new Cat(); // throws ArrayStoreException at runtime

यह जेनेरिक के लिए इसके विपरीत है:
जेनेरिक erased और invariant
इसलिए जेनेरिक रनटाइम प्रकार की सुरक्षा प्रदान नहीं कर सकते हैं, लेकिन वे संकलन-समय प्रकार की सुरक्षा प्रदान करते हैं।
नीचे दिए गए कोड में यदि जेनेरिक कॉन्वर्सेंट थे तो लाइन 3 पर ढेर प्रदूषण करना संभव होगा।

    List<Dog> dogs = new ArrayList<>();
    List<Animal> animals = dogs; // compile-time error, otherwise heap pollution
    animals.add(new Cat());

List<Dog> एक List<Animal> , उदाहरण के लिए, आप एक Cat List<Animal> में एक Cat डाल सकते हैं, लेकिन List<Dog> में नहीं List<Dog> ... आप बनाने के लिए वाइल्डकार्ड का उपयोग कर सकते हैं जहां संभव हो वहां जेनेरिक अधिक एक्स्टेंसिबल; उदाहरण के लिए, एक List<Dog> से पढ़ना एक List<Animal> से पढ़ने के समान है - लेकिन लिखना नहीं।

जावा ट्यूटोरियल से जेनिक्स में जेनिक्स और जेनरिक पर जेनरिक्स में बहुत अच्छी, गहराई से स्पष्टीकरण है कि क्यों कुछ चीजें पॉलिमॉर्फिक या जेनेरिक के साथ अनुमति नहीं हैं।


answer साथ ही अन्य उत्तरों सही हैं। मैं उन उत्तरों में एक समाधान के साथ जोड़ने जा रहा हूं जो मुझे लगता है कि मददगार होगा। मुझे लगता है कि यह अक्सर प्रोग्रामिंग में आता है। ध्यान देने योग्य बात यह है कि संग्रह (सूची, समूह, आदि) के लिए मुख्य मुद्दा संग्रह में जोड़ रहा है। यही वह जगह है जहां चीजें टूट जाती हैं। यहां तक ​​कि निकालना ठीक है।

ज्यादातर मामलों में, हम Collection<? extends T> उपयोग कर सकते हैं Collection<? extends T> Collection<? extends T> बल्कि तब Collection<T> Collection<? extends T> और यह पहली पसंद होना चाहिए। हालांकि, मुझे ऐसे मामले मिल रहे हैं जहां ऐसा करना आसान नहीं है। यह बहस के लिए है कि क्या यह हमेशा करने के लिए सबसे अच्छी बात है। मैं यहां एक क्लास डाउनकास्टकोलेक्शन प्रस्तुत कर रहा हूं जो Collection<? extends T> परिवर्तित कर सकता है Collection<? extends T> एक Collection<T> Collection<? extends T> को Collection<? extends T> Collection<T> (हम सूची, सेट, नेविगेलसेट, .. के लिए समान वर्गों को परिभाषित कर सकते हैं) मानक दृष्टिकोण का उपयोग करते समय उपयोग किए जाने के लिए बहुत ही असुविधाजनक है। नीचे इसका उपयोग करने का एक उदाहरण है (हम इस मामले में Collection<? extends Object> भी कर सकते हैं, लेकिन मैं डाउनकास्टकोलेक्शन का उपयोग करके इसे सरल बनाने के लिए सरल रख रहा हूं।

/**Could use Collection<? extends Object> and that is the better choice. 
* But I am doing this to illustrate how to use DownCastCollection. **/

public static void print(Collection<Object> col){  
    for(Object obj : col){
    System.out.println(obj);
    }
}
public static void main(String[] args){
  ArrayList<String> list = new ArrayList<>();
  list.addAll(Arrays.asList("a","b","c"));
  print(new DownCastCollection<Object>(list));
}

अब वर्ग:

import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class DownCastCollection<E> extends AbstractCollection<E> implements Collection<E> {
private Collection<? extends E> delegate;

public DownCastCollection(Collection<? extends E> delegate) {
    super();
    this.delegate = delegate;
}

@Override
public int size() {
    return delegate ==null ? 0 : delegate.size();
}

@Override
public boolean isEmpty() {
    return delegate==null || delegate.isEmpty();
}

@Override
public boolean contains(Object o) {
    if(isEmpty()) return false;
    return delegate.contains(o);
}
private class MyIterator implements Iterator<E>{
    Iterator<? extends E> delegateIterator;

    protected MyIterator() {
        super();
        this.delegateIterator = delegate == null ? null :delegate.iterator();
    }

    @Override
    public boolean hasNext() {
        return delegateIterator != null && delegateIterator.hasNext();
    }

    @Override
    public  E next() {
        if(!hasNext()) throw new NoSuchElementException("The iterator is empty");
        return delegateIterator.next();
    }

    @Override
    public void remove() {
        delegateIterator.remove();

    }

}
@Override
public Iterator<E> iterator() {
    return new MyIterator();
}



@Override
public boolean add(E e) {
    throw new UnsupportedOperationException();
}

@Override
public boolean remove(Object o) {
    if(delegate == null) return false;
    return delegate.remove(o);
}

@Override
public boolean containsAll(Collection<?> c) {
    if(delegate==null) return false;
    return delegate.containsAll(c);
}

@Override
public boolean addAll(Collection<? extends E> c) {
    throw new UnsupportedOperationException();
}

@Override
public boolean removeAll(Collection<?> c) {
    if(delegate == null) return false;
    return delegate.removeAll(c);
}

@Override
public boolean retainAll(Collection<?> c) {
    if(delegate == null) return false;
    return delegate.retainAll(c);
}

@Override
public void clear() {
    if(delegate == null) return;
        delegate.clear();

}

}







polymorphism