Is List a subclass of List? Why are Java generics not implicitly polymorphic?











up vote
648
down vote

favorite
220












I'm a bit confused about how Java generics handle inheritance / polymorphism.



Assume the following hierarchy -



Animal (Parent)



Dog - Cat (Children)



So suppose I have a method doSomething(List<Animal> animals). By all the rules of inheritance and polymorphism, I would assume that a List<Dog> is a List<Animal> and a List<Cat> is a List<Animal> - and so either one could be passed to this method. Not so. If I want to achieve this behavior, I have to explicitly tell the method to accept a list of any subclass of Animal by saying doSomething(List<? extends Animal> animals).



I understand that this is Java's behavior. My question is why? Why is polymorphism generally implicit, but when it comes to generics it must be specified?










share|improve this question




















  • 13




    And a totally unrelated grammar question that's bothering me now - should my title be "why aren't Java's generics" or "why isn't Java's generics"?? Is "generics" plural because of the s or singular because it's one entity?
    – froadie
    Apr 30 '10 at 14:44






  • 21




    generics as done in Java are a very poor form of parametric polymorphism. Don't put too much into faith into them (like I used to), because one day you'll hit hard their pathetic limitations: Surgeon extends Handable<Scalpel>, Handable<Sponge> KABOOM! Does not compute [TM]. There's your Java generics limitation. Any OOA/OOD can be translated fine into Java (and MI can be done very nicely using Java interfaces) but generics just don't cut it. They're fine for "collections" and procedural programming that said (which is what most Java programmers do anyway so...).
    – SyntaxT3rr0r
    Apr 30 '10 at 15:43






  • 7




    Super class of List<Dog> is not List<Animal> but List<?> (i.e list of unknown type) . Generics erases type information in compiled code. This is done so that code which is using generics(java 5 & above) is compatible with earlier versions of java without generics.
    – rai.skumar
    Dec 4 '12 at 11:15








  • 3




    Related SO question - Whats the use of saying <? extends SomeObject> instead of <SomeObject>
    – Aniket Thakur
    Oct 2 '15 at 18:53






  • 7




    @froadie since nobody seemed to respond... it should definitely be "why aren't Java's generics...". The other issue is that "generic" is actually an adjective, and so "generics" is referring to a dropped plural noun modified by "generic". You could say "that function is a generic", but that would be more cumbersome than saying "that function is generic". However, it's a bit cumbersome to say "Java has generic functions and classes", instead of just "Java has generics". As someone who wrote their master's thesis on adjectives, I think you've stumbled upon a very interesting question!
    – dantiston
    May 30 '17 at 5:18















up vote
648
down vote

favorite
220












I'm a bit confused about how Java generics handle inheritance / polymorphism.



Assume the following hierarchy -



Animal (Parent)



Dog - Cat (Children)



So suppose I have a method doSomething(List<Animal> animals). By all the rules of inheritance and polymorphism, I would assume that a List<Dog> is a List<Animal> and a List<Cat> is a List<Animal> - and so either one could be passed to this method. Not so. If I want to achieve this behavior, I have to explicitly tell the method to accept a list of any subclass of Animal by saying doSomething(List<? extends Animal> animals).



I understand that this is Java's behavior. My question is why? Why is polymorphism generally implicit, but when it comes to generics it must be specified?










share|improve this question




















  • 13




    And a totally unrelated grammar question that's bothering me now - should my title be "why aren't Java's generics" or "why isn't Java's generics"?? Is "generics" plural because of the s or singular because it's one entity?
    – froadie
    Apr 30 '10 at 14:44






  • 21




    generics as done in Java are a very poor form of parametric polymorphism. Don't put too much into faith into them (like I used to), because one day you'll hit hard their pathetic limitations: Surgeon extends Handable<Scalpel>, Handable<Sponge> KABOOM! Does not compute [TM]. There's your Java generics limitation. Any OOA/OOD can be translated fine into Java (and MI can be done very nicely using Java interfaces) but generics just don't cut it. They're fine for "collections" and procedural programming that said (which is what most Java programmers do anyway so...).
    – SyntaxT3rr0r
    Apr 30 '10 at 15:43






  • 7




    Super class of List<Dog> is not List<Animal> but List<?> (i.e list of unknown type) . Generics erases type information in compiled code. This is done so that code which is using generics(java 5 & above) is compatible with earlier versions of java without generics.
    – rai.skumar
    Dec 4 '12 at 11:15








  • 3




    Related SO question - Whats the use of saying <? extends SomeObject> instead of <SomeObject>
    – Aniket Thakur
    Oct 2 '15 at 18:53






  • 7




    @froadie since nobody seemed to respond... it should definitely be "why aren't Java's generics...". The other issue is that "generic" is actually an adjective, and so "generics" is referring to a dropped plural noun modified by "generic". You could say "that function is a generic", but that would be more cumbersome than saying "that function is generic". However, it's a bit cumbersome to say "Java has generic functions and classes", instead of just "Java has generics". As someone who wrote their master's thesis on adjectives, I think you've stumbled upon a very interesting question!
    – dantiston
    May 30 '17 at 5:18













up vote
648
down vote

favorite
220









up vote
648
down vote

favorite
220






220





I'm a bit confused about how Java generics handle inheritance / polymorphism.



Assume the following hierarchy -



Animal (Parent)



Dog - Cat (Children)



So suppose I have a method doSomething(List<Animal> animals). By all the rules of inheritance and polymorphism, I would assume that a List<Dog> is a List<Animal> and a List<Cat> is a List<Animal> - and so either one could be passed to this method. Not so. If I want to achieve this behavior, I have to explicitly tell the method to accept a list of any subclass of Animal by saying doSomething(List<? extends Animal> animals).



I understand that this is Java's behavior. My question is why? Why is polymorphism generally implicit, but when it comes to generics it must be specified?










share|improve this question















I'm a bit confused about how Java generics handle inheritance / polymorphism.



Assume the following hierarchy -



Animal (Parent)



Dog - Cat (Children)



So suppose I have a method doSomething(List<Animal> animals). By all the rules of inheritance and polymorphism, I would assume that a List<Dog> is a List<Animal> and a List<Cat> is a List<Animal> - and so either one could be passed to this method. Not so. If I want to achieve this behavior, I have to explicitly tell the method to accept a list of any subclass of Animal by saying doSomething(List<? extends Animal> animals).



I understand that this is Java's behavior. My question is why? Why is polymorphism generally implicit, but when it comes to generics it must be specified?







java generics inheritance polymorphism






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 20 at 9:22









cellepo

1,32611528




1,32611528










asked Apr 30 '10 at 14:39









froadie

34.3k58146210




34.3k58146210








  • 13




    And a totally unrelated grammar question that's bothering me now - should my title be "why aren't Java's generics" or "why isn't Java's generics"?? Is "generics" plural because of the s or singular because it's one entity?
    – froadie
    Apr 30 '10 at 14:44






  • 21




    generics as done in Java are a very poor form of parametric polymorphism. Don't put too much into faith into them (like I used to), because one day you'll hit hard their pathetic limitations: Surgeon extends Handable<Scalpel>, Handable<Sponge> KABOOM! Does not compute [TM]. There's your Java generics limitation. Any OOA/OOD can be translated fine into Java (and MI can be done very nicely using Java interfaces) but generics just don't cut it. They're fine for "collections" and procedural programming that said (which is what most Java programmers do anyway so...).
    – SyntaxT3rr0r
    Apr 30 '10 at 15:43






  • 7




    Super class of List<Dog> is not List<Animal> but List<?> (i.e list of unknown type) . Generics erases type information in compiled code. This is done so that code which is using generics(java 5 & above) is compatible with earlier versions of java without generics.
    – rai.skumar
    Dec 4 '12 at 11:15








  • 3




    Related SO question - Whats the use of saying <? extends SomeObject> instead of <SomeObject>
    – Aniket Thakur
    Oct 2 '15 at 18:53






  • 7




    @froadie since nobody seemed to respond... it should definitely be "why aren't Java's generics...". The other issue is that "generic" is actually an adjective, and so "generics" is referring to a dropped plural noun modified by "generic". You could say "that function is a generic", but that would be more cumbersome than saying "that function is generic". However, it's a bit cumbersome to say "Java has generic functions and classes", instead of just "Java has generics". As someone who wrote their master's thesis on adjectives, I think you've stumbled upon a very interesting question!
    – dantiston
    May 30 '17 at 5:18














  • 13




    And a totally unrelated grammar question that's bothering me now - should my title be "why aren't Java's generics" or "why isn't Java's generics"?? Is "generics" plural because of the s or singular because it's one entity?
    – froadie
    Apr 30 '10 at 14:44






  • 21




    generics as done in Java are a very poor form of parametric polymorphism. Don't put too much into faith into them (like I used to), because one day you'll hit hard their pathetic limitations: Surgeon extends Handable<Scalpel>, Handable<Sponge> KABOOM! Does not compute [TM]. There's your Java generics limitation. Any OOA/OOD can be translated fine into Java (and MI can be done very nicely using Java interfaces) but generics just don't cut it. They're fine for "collections" and procedural programming that said (which is what most Java programmers do anyway so...).
    – SyntaxT3rr0r
    Apr 30 '10 at 15:43






  • 7




    Super class of List<Dog> is not List<Animal> but List<?> (i.e list of unknown type) . Generics erases type information in compiled code. This is done so that code which is using generics(java 5 & above) is compatible with earlier versions of java without generics.
    – rai.skumar
    Dec 4 '12 at 11:15








  • 3




    Related SO question - Whats the use of saying <? extends SomeObject> instead of <SomeObject>
    – Aniket Thakur
    Oct 2 '15 at 18:53






  • 7




    @froadie since nobody seemed to respond... it should definitely be "why aren't Java's generics...". The other issue is that "generic" is actually an adjective, and so "generics" is referring to a dropped plural noun modified by "generic". You could say "that function is a generic", but that would be more cumbersome than saying "that function is generic". However, it's a bit cumbersome to say "Java has generic functions and classes", instead of just "Java has generics". As someone who wrote their master's thesis on adjectives, I think you've stumbled upon a very interesting question!
    – dantiston
    May 30 '17 at 5:18








13




13




And a totally unrelated grammar question that's bothering me now - should my title be "why aren't Java's generics" or "why isn't Java's generics"?? Is "generics" plural because of the s or singular because it's one entity?
– froadie
Apr 30 '10 at 14:44




And a totally unrelated grammar question that's bothering me now - should my title be "why aren't Java's generics" or "why isn't Java's generics"?? Is "generics" plural because of the s or singular because it's one entity?
– froadie
Apr 30 '10 at 14:44




21




21




generics as done in Java are a very poor form of parametric polymorphism. Don't put too much into faith into them (like I used to), because one day you'll hit hard their pathetic limitations: Surgeon extends Handable<Scalpel>, Handable<Sponge> KABOOM! Does not compute [TM]. There's your Java generics limitation. Any OOA/OOD can be translated fine into Java (and MI can be done very nicely using Java interfaces) but generics just don't cut it. They're fine for "collections" and procedural programming that said (which is what most Java programmers do anyway so...).
– SyntaxT3rr0r
Apr 30 '10 at 15:43




generics as done in Java are a very poor form of parametric polymorphism. Don't put too much into faith into them (like I used to), because one day you'll hit hard their pathetic limitations: Surgeon extends Handable<Scalpel>, Handable<Sponge> KABOOM! Does not compute [TM]. There's your Java generics limitation. Any OOA/OOD can be translated fine into Java (and MI can be done very nicely using Java interfaces) but generics just don't cut it. They're fine for "collections" and procedural programming that said (which is what most Java programmers do anyway so...).
– SyntaxT3rr0r
Apr 30 '10 at 15:43




7




7




Super class of List<Dog> is not List<Animal> but List<?> (i.e list of unknown type) . Generics erases type information in compiled code. This is done so that code which is using generics(java 5 & above) is compatible with earlier versions of java without generics.
– rai.skumar
Dec 4 '12 at 11:15






Super class of List<Dog> is not List<Animal> but List<?> (i.e list of unknown type) . Generics erases type information in compiled code. This is done so that code which is using generics(java 5 & above) is compatible with earlier versions of java without generics.
– rai.skumar
Dec 4 '12 at 11:15






3




3




Related SO question - Whats the use of saying <? extends SomeObject> instead of <SomeObject>
– Aniket Thakur
Oct 2 '15 at 18:53




Related SO question - Whats the use of saying <? extends SomeObject> instead of <SomeObject>
– Aniket Thakur
Oct 2 '15 at 18:53




7




7




@froadie since nobody seemed to respond... it should definitely be "why aren't Java's generics...". The other issue is that "generic" is actually an adjective, and so "generics" is referring to a dropped plural noun modified by "generic". You could say "that function is a generic", but that would be more cumbersome than saying "that function is generic". However, it's a bit cumbersome to say "Java has generic functions and classes", instead of just "Java has generics". As someone who wrote their master's thesis on adjectives, I think you've stumbled upon a very interesting question!
– dantiston
May 30 '17 at 5:18




@froadie since nobody seemed to respond... it should definitely be "why aren't Java's generics...". The other issue is that "generic" is actually an adjective, and so "generics" is referring to a dropped plural noun modified by "generic". You could say "that function is a generic", but that would be more cumbersome than saying "that function is generic". However, it's a bit cumbersome to say "Java has generic functions and classes", instead of just "Java has generics". As someone who wrote their master's thesis on adjectives, I think you've stumbled upon a very interesting question!
– dantiston
May 30 '17 at 5:18












16 Answers
16






active

oldest

votes

















up vote
791
down vote



accepted










No, a List<Dog> is not a List<Animal>. Consider what you can do with a List<Animal> - you can add any animal to it... including a cat. Now, can you logically add a cat to a litter of puppies? Absolutely not.



// Illegal code - because otherwise life would be Bad
List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?


Suddenly you have a very confused cat.



Now, you can't add a Cat to a List<? extends Animal> because you don't know it's a List<Cat>. You can retrieve a value and know that it will be an Animal, but you can't add arbitrary animals. The reverse is true for List<? super Animal> - in that case you can add an Animal to it safely, but you don't know anything about what might be retrieved from it, because it could be a List<Object>.






share|improve this answer



















  • 37




    Interestingly, every list of dogs is indeed a list of animals, just like intuition tells us. The point is, that not every list of animals is a list of dogs, hence mutattion of the list by adding a cat is the problem.
    – Ingo
    Jan 28 '13 at 19:29






  • 43




    @Ingo: No, not really: you can add a cat to a list of animals, but you can't add a cat to a list of dogs. A list of dogs is only a list of animals if you consider it in a read-only sense.
    – Jon Skeet
    Jan 28 '13 at 19:33






  • 9




    @JonSkeet - Of course, but who is mandating that making a new list from a cat and a list of dogs actually changes the list of dogs? This is an arbitrary implementation decision in Java. One that goes counter to logic and intuition.
    – Ingo
    Jan 28 '13 at 19:41






  • 6




    @Ingo: I wouldn't have used that "certainly" to start with. If you have a list which says at the top "Hotels we might want to go to" and then someone added a swimming pool to it, would you think that valid? No - it's a list of hotels, which isn't a list of buildings. And it's not like I even said "A list of dogs is not a list of animals" - I put it in code terms, in a code font. I really don't think there's any ambiguity here. Using subclass would be incorrect anyway - it's about assignment compatibility, not subclassing.
    – Jon Skeet
    Jan 28 '13 at 19:58






  • 11




    @ruakh: The problem is that you're then punting to execution time something which can be blocked at compile-time. And I'd argue that array covariance was a design mistake to start with.
    – Jon Skeet
    Jul 3 '13 at 17:22


















up vote
70
down vote













What you are looking for is called covariant type parameters. This means that if one type of object can be substituted for another in a method (for instance, Animal can be replaced with Dog), the same applies to expressions using those objects (so List<Animal> could be replaced with List<Dog>). The problem is that covariance is not safe for mutable lists in general. Suppose you have a List<Dog>, and it is being used as a List<Animal>. What happens when you try to add a Cat to this List<Animal> which is really a List<Dog>? Automatically allowing type parameters to be covariant breaks the type system.



It would be useful to add syntax to allow type parameters to be specified as covariant, which avoids the ? extends Foo in method declarations, but that does add additional complexity.






share|improve this answer























  • Or use: List<Object>
    – Mo'in Creemers
    Aug 29 '12 at 11:35




















up vote
42
down vote













The reason a List<Dog> is not a List<Animal>, is that, for example, you can insert a Cat into a List<Animal>, but not into a List<Dog>... you can use wildcards to make generics more extensible where possible; for example, reading from a List<Dog> is the similar to reading from a List<Animal> -- but not writing.



The Generics in the Java Language and the Section on Generics from the Java Tutorials have a very good, in-depth explanation as to why some things are or are not polymorphic or permitted with generics.






share|improve this answer






























    up vote
    32
    down vote













    I would say the whole point of Generics is that it doesn't allow that. Consider the situation with arrays, which do allow that type of covariance:



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


    That code compiles fine, but throws a runtime error (java.lang.ArrayStoreException: java.lang.Boolean in the second line). It is not typesafe. The point of Generics is to add the compile time type safety, otherwise you could just stick with a plain class without generics.



    Now there are times where you need to be more flexible and that is what the ? super Class and ? extends Class are for. The former is when you need to insert into a type Collection (for example), and the latter is for when you need to read from it, in a type safe manner. But the only way to do both at the same time is to have a specific type.






    share|improve this answer



















    • 12




      Arguably, array covariance is a language design bug. Note that due to type erasure, the same behaviour is technically impossible for generic collection.
      – Michael Borgwardt
      Apr 30 '10 at 14:55










    • FYI: I mentioned your answer in stackoverflow.com/a/26551453/295802
      – Mark Bennett
      Oct 24 '14 at 17:16










    • "I would say the whole point of Generics is that it doesn't allow that.". You can never be sure: Java and Scala's Type Systems are Unsound: The Existential Crisis of Null Pointers (presented at OOPSLA 2016) (since corrected it seems)
      – David Tonhofer
      Aug 15 '17 at 17:52


















    up vote
    28
    down vote













    A point I think should be added to what other answers mention is that while




    List<Dog> isn't-a List<Animal> in Java




    it is also true that




    A list of dogs is-a list of animals in English (well, under a reasonable interpretation)




    The way the OP's intuition works - which is completely valid of course - is the latter sentence. However, if we apply this intuition we get a language that is not Java-esque in its type system: Suppose our language does allow adding a cat to our list of dogs. What would that mean? It would mean that the list ceases to be a list of dogs, and remains merely a list of animals. And a list of mammals, and a list of quadrapeds.



    To put it another way: A List<Dog> in Java does not mean "a list of dogs" in English, it means "a list which can have dogs, and nothing else".



    More generally, OP's intuition lends itself towards a language in which operations on objects can change their type, or rather, an object's type(s) is a (dynamic) function of its value.






    share|improve this answer























    • Yes, human language is more fuzzy. But still, once you add a different animal to the list of dogs, it is still a list of animals, but no longer a list of dogs. The difference being, a human, with the fuzzy logic, usually has no problem realizing that.
      – Vlasec
      Nov 13 '17 at 12:14




















    up vote
    7
    down vote













    To understand the problem it's useful to make comparison to arrays.



    List<Dog> is not subclass of List<Animal>.
    But Dog is subclass of Animal.



    Arrays are reifiable and covariant.
    Reifiable means their type information is fully available at runtime.
    Therefore arrays provide runtime type safety but not compile-time type safety.



        // 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


    It's vice versa for generics:
    Generics are erased and invariant.
    Therefore generics can't provide runtime type safety, but they provide compile-time type safety.
    In the code below if generics were covariant it will be possible to make heap pollution at line 3.



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





    share|improve this answer



















    • 2




      It might be argued that, precisely because of that, Arrays in Java are broken,
      – leonbloy
      Dec 15 '17 at 14:41










    • Arrays being covariant is a compiler "feature".
      – Cristik
      Mar 2 at 7:40


















    up vote
    4
    down vote













    The answers given here didn't fully convince me. So instead, I make another example.



    public void passOn(Consumer<Animal> consumer, Supplier<Animal> supplier) {
    consumer.accept(supplier.get());
    }


    sounds fine, doesn't it? But you can only pass Consumers and Suppliers for Animals. If you have a Mammal consumer, but a Duck supplier, they should not fit although both are animals. In order to disallow this, additional restrictions have been added.



    Instead of the above, we have to define relationships between the types we use.



    E. g.,



    public <A extends Animal> void passOn(Consumer<A> consumer, Supplier<? extends A> supplier) {
    consumer.accept(supplier.get());
    }


    makes sure that we can only use a supplier which provides us the right type of object for the consumer.



    OTOH, we could as well do



    public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<A> supplier) {
    consumer.accept(supplier.get());
    }


    where we go the other way: we define the type of the Supplier and restrict that it can be put into the Consumer.



    We even can do



    public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<? extends A> supplier) {
    consumer.accept(supplier.get());
    }


    where, having the intuitive relations Life -> Animal -> Mammal -> Dog, Cat etc., we could even put a Mammal into a Life consumer, but not a String into a Life consumer.






    share|improve this answer

















    • 1




      Among the 4 versions, #2 is probably incorrect. e.g. we cannot call it with (Consumer<Runnable>, Supplier<Dog>) while Dog is subtype of Animal & Runnable
      – ZhongYu
      Jul 27 '15 at 20:10












    • groups.google.com/forum/#!topic/java-lang-fans/0GDv0salPTs
      – ZhongYu
      Jul 27 '15 at 20:39


















    up vote
    4
    down vote













    The basis logic for such behavior is that Generics follow a mechanism of type erasure. So at run time you have no way if identifying the type of collection unlike arrays where there is no such erasure process. So coming back to your question...



    So suppose there is a method as given below:



    add(List<Animal>){
    //You can add List<Dog or List<Cat> and this will compile as per rules of polymorphism
    }


    Now if java allows caller to add List of type Animal to this method then you might add wrong thing into collection and at run time too it will run due to type erasure. While in case of arrays you will get a run time exception for such scenarios...



    Thus in essence this behavior is implemented so that one cannot add wrong thing into collection. Now I believe type erasure exists so as to give compatibility with legacy java without generics....






    share|improve this answer






























      up vote
      3
      down vote













      Actually you can use an interface to achieve what you want.



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


      }



      you can then use the collections using



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





      share|improve this answer




























        up vote
        1
        down vote













        If you are sure that the list items are subclasses of that given super type you can cast the list using this approach:



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


        This is usefull when you want to pass the list in a constructor or iterate over it






        share|improve this answer

















        • 2




          This will create more problems than it actually solves
          – Ferrybig
          Feb 1 '16 at 15:33










        • If you try to add a Cat to the list, sure it will create problems, but for looping purposes i think its the only non verbose answer.
          – sagits
          Feb 2 '16 at 12:28


















        up vote
        1
        down vote













        The answer as well as other answers are correct. I am going to add to those answers with a solution that I think will be helpful. I think this comes up often in programming. One thing to note is that for Collections (Lists, Sets, etc.) the main issue is adding to the Collection. That is where things break down. Even removing is OK.



        In most cases, we can use Collection<? extends T> rather then Collection<T> and that should be the first choice. However, I am finding cases where it is not easy to do that. It is up for debate as to whether that is always the best thing to do. I am presenting here a class DownCastCollection that can take convert a Collection<? extends T> to a Collection<T> (we can define similar classes for List, Set, NavigableSet,..) to be used when using the standard approach is very inconvenient. Below is an example of how to use it (we could also use Collection<? extends Object> in this case, but I am keeping it simple to illustrate using DownCastCollection.



        /**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));
        }


        Now the class:



        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();

        }


        }






        share|improve this answer























        • This is a good idea, so much so that it exists in Java SE already. ; ) Collections.unmodifiableCollection
          – Radiodef
          May 8 '15 at 21:30






        • 1




          Right but the collection I define can be modified.
          – dan b
          May 9 '15 at 11:40










        • Yes, it can be modified. Collection<? extends E> already handles that behavior correctly though, unless you use it in a way that is not type-safe (e.g. casting it to something else). The only advantage I see there is, when you call the add operation, it throws an exception even if you casted it.
          – Vlasec
          Nov 13 '17 at 12:47


















        up vote
        1
        down vote













        Subtyping is invariant for parameterized types. Even tough the class Dog is a subtype of Animal, the parameterized type List<Dog> is not a subtype of List<Animal>. In contrast, covariant subtyping is used by arrays, so the array
        type Dog is a subtype of Animal.



        Invariant subtyping ensures that the type constraints enforced by Java are not violated. Consider the following code given by @Jon Skeet:



        List<Dog> dogs = new ArrayList<Dog>(1);
        List<Animal> animals = dogs;
        animals.add(new Cat()); // compile-time error
        Dog dog = dogs.get(0);


        As stated by @Jon Skeet, this code is illegal, because otherwise it would violate the type constraints by returning a cat when a dog expected.



        It is instructive to compare the above to analogous code for arrays.



        Dog dogs = new Dog[1];
        Object animals = dogs;
        animals[0] = new Cat(); // run-time error
        Dog dog = dogs[0];


        The code is legal. However, throws an array store exception.
        An array carries its type at run-time this way JVM can enforce
        type safety of covariant subtyping.



        To understand this further let's look at the bytecode generated by javap of the class below:



        import java.util.ArrayList;
        import java.util.List;

        public class Demonstration {
        public void normal() {
        List normal = new ArrayList(1);
        normal.add("lorem ipsum");
        }

        public void parameterized() {
        List<String> parameterized = new ArrayList<>(1);
        parameterized.add("lorem ipsum");
        }
        }


        Using the command javap -c Demonstration, this shows the following Java bytecode:



        Compiled from "Demonstration.java"
        public class Demonstration {
        public Demonstration();
        Code:
        0: aload_0
        1: invokespecial #1 // Method java/lang/Object."<init>":()V
        4: return

        public void normal();
        Code:
        0: new #2 // class java/util/ArrayList
        3: dup
        4: iconst_1
        5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
        8: astore_1
        9: aload_1
        10: ldc #4 // String lorem ipsum
        12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
        17: pop
        18: return

        public void parameterized();
        Code:
        0: new #2 // class java/util/ArrayList
        3: dup
        4: iconst_1
        5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
        8: astore_1
        9: aload_1
        10: ldc #4 // String lorem ipsum
        12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
        17: pop
        18: return
        }


        Observe that the translated code of method bodies are identical. Compiler replaced each parameterized type by its erasure. This property is crucial meaning that it did not break backwards compatibility.



        In conclusion, run-time safety is not possible for parameterized types, since compiler replaces each parameterized type by its erasure. This makes parameterized types are nothing more than syntactic sugar.






        share|improve this answer




























          up vote
          0
          down vote













          Lets take the example from JavaSE 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) {
          ...
          }
          }


          So why a list of dogs (circles) should not be considered implicitly a list of animals (shapes) is because of this situation:



          // drawAll method call
          drawAll(circleList);


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


          So Java "architects" had 2 options which address this problem:




          1. do not consider that a subtype is implicitly it's supertype, and give a compile error, like it happens now


          2. consider the subtype to be it's supertype and restrict at compile the "add" method (so in the drawAll method, if a list of circles, subtype of shape, would be passed, the compiler should detected that and restrict you with compile error into doing that).



          For obvious reasons, that chose the first way.






          share|improve this answer




























            up vote
            0
            down vote













            We should also take in consideration how the compiler threats the generic classes: in "instantiates" a different type whenever we fill the generic arguments.



            Thus we have ListOfAnimal, ListOfDog, ListOfCat, etc, which are distinct classes that end up being "created" by the compiler when we specify the generic arguments. And this is a flat hierarchy (actually regarding to List is not a hierarchy at all).



            Another argument why covariance doesn't make sense in case of generic classes is the fact that at base all classes are the same - are List instances. Specialising a List by filling the generic argument doesn't extend the class, it just makes it work for that particular generic argument.






            share|improve this answer




























              up vote
              0
              down vote













              The problem has been well-identified. But there's a solution; make doSomething generic:



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


              now you can call doSomething with either List<Dog> or List<Cat> or List<Animal>.






              share|improve this answer




























                up vote
                0
                down vote













                another solution is to build a new list



                List<Dog> dogs = new ArrayList<Dog>(); 
                List<Animal> animals = new ArrayList<Animal>(dogs);
                animals.add(new Cat());





                share|improve this answer




















                  protected by Aniket Thakur Oct 2 '15 at 18:51



                  Thank you for your interest in this question.
                  Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).



                  Would you like to answer one of these unanswered questions instead?














                  16 Answers
                  16






                  active

                  oldest

                  votes








                  16 Answers
                  16






                  active

                  oldest

                  votes









                  active

                  oldest

                  votes






                  active

                  oldest

                  votes








                  up vote
                  791
                  down vote



                  accepted










                  No, a List<Dog> is not a List<Animal>. Consider what you can do with a List<Animal> - you can add any animal to it... including a cat. Now, can you logically add a cat to a litter of puppies? Absolutely not.



                  // Illegal code - because otherwise life would be Bad
                  List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
                  List<Animal> animals = dogs; // Awooga awooga
                  animals.add(new Cat());
                  Dog dog = dogs.get(0); // This should be safe, right?


                  Suddenly you have a very confused cat.



                  Now, you can't add a Cat to a List<? extends Animal> because you don't know it's a List<Cat>. You can retrieve a value and know that it will be an Animal, but you can't add arbitrary animals. The reverse is true for List<? super Animal> - in that case you can add an Animal to it safely, but you don't know anything about what might be retrieved from it, because it could be a List<Object>.






                  share|improve this answer



















                  • 37




                    Interestingly, every list of dogs is indeed a list of animals, just like intuition tells us. The point is, that not every list of animals is a list of dogs, hence mutattion of the list by adding a cat is the problem.
                    – Ingo
                    Jan 28 '13 at 19:29






                  • 43




                    @Ingo: No, not really: you can add a cat to a list of animals, but you can't add a cat to a list of dogs. A list of dogs is only a list of animals if you consider it in a read-only sense.
                    – Jon Skeet
                    Jan 28 '13 at 19:33






                  • 9




                    @JonSkeet - Of course, but who is mandating that making a new list from a cat and a list of dogs actually changes the list of dogs? This is an arbitrary implementation decision in Java. One that goes counter to logic and intuition.
                    – Ingo
                    Jan 28 '13 at 19:41






                  • 6




                    @Ingo: I wouldn't have used that "certainly" to start with. If you have a list which says at the top "Hotels we might want to go to" and then someone added a swimming pool to it, would you think that valid? No - it's a list of hotels, which isn't a list of buildings. And it's not like I even said "A list of dogs is not a list of animals" - I put it in code terms, in a code font. I really don't think there's any ambiguity here. Using subclass would be incorrect anyway - it's about assignment compatibility, not subclassing.
                    – Jon Skeet
                    Jan 28 '13 at 19:58






                  • 11




                    @ruakh: The problem is that you're then punting to execution time something which can be blocked at compile-time. And I'd argue that array covariance was a design mistake to start with.
                    – Jon Skeet
                    Jul 3 '13 at 17:22















                  up vote
                  791
                  down vote



                  accepted










                  No, a List<Dog> is not a List<Animal>. Consider what you can do with a List<Animal> - you can add any animal to it... including a cat. Now, can you logically add a cat to a litter of puppies? Absolutely not.



                  // Illegal code - because otherwise life would be Bad
                  List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
                  List<Animal> animals = dogs; // Awooga awooga
                  animals.add(new Cat());
                  Dog dog = dogs.get(0); // This should be safe, right?


                  Suddenly you have a very confused cat.



                  Now, you can't add a Cat to a List<? extends Animal> because you don't know it's a List<Cat>. You can retrieve a value and know that it will be an Animal, but you can't add arbitrary animals. The reverse is true for List<? super Animal> - in that case you can add an Animal to it safely, but you don't know anything about what might be retrieved from it, because it could be a List<Object>.






                  share|improve this answer



















                  • 37




                    Interestingly, every list of dogs is indeed a list of animals, just like intuition tells us. The point is, that not every list of animals is a list of dogs, hence mutattion of the list by adding a cat is the problem.
                    – Ingo
                    Jan 28 '13 at 19:29






                  • 43




                    @Ingo: No, not really: you can add a cat to a list of animals, but you can't add a cat to a list of dogs. A list of dogs is only a list of animals if you consider it in a read-only sense.
                    – Jon Skeet
                    Jan 28 '13 at 19:33






                  • 9




                    @JonSkeet - Of course, but who is mandating that making a new list from a cat and a list of dogs actually changes the list of dogs? This is an arbitrary implementation decision in Java. One that goes counter to logic and intuition.
                    – Ingo
                    Jan 28 '13 at 19:41






                  • 6




                    @Ingo: I wouldn't have used that "certainly" to start with. If you have a list which says at the top "Hotels we might want to go to" and then someone added a swimming pool to it, would you think that valid? No - it's a list of hotels, which isn't a list of buildings. And it's not like I even said "A list of dogs is not a list of animals" - I put it in code terms, in a code font. I really don't think there's any ambiguity here. Using subclass would be incorrect anyway - it's about assignment compatibility, not subclassing.
                    – Jon Skeet
                    Jan 28 '13 at 19:58






                  • 11




                    @ruakh: The problem is that you're then punting to execution time something which can be blocked at compile-time. And I'd argue that array covariance was a design mistake to start with.
                    – Jon Skeet
                    Jul 3 '13 at 17:22













                  up vote
                  791
                  down vote



                  accepted







                  up vote
                  791
                  down vote



                  accepted






                  No, a List<Dog> is not a List<Animal>. Consider what you can do with a List<Animal> - you can add any animal to it... including a cat. Now, can you logically add a cat to a litter of puppies? Absolutely not.



                  // Illegal code - because otherwise life would be Bad
                  List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
                  List<Animal> animals = dogs; // Awooga awooga
                  animals.add(new Cat());
                  Dog dog = dogs.get(0); // This should be safe, right?


                  Suddenly you have a very confused cat.



                  Now, you can't add a Cat to a List<? extends Animal> because you don't know it's a List<Cat>. You can retrieve a value and know that it will be an Animal, but you can't add arbitrary animals. The reverse is true for List<? super Animal> - in that case you can add an Animal to it safely, but you don't know anything about what might be retrieved from it, because it could be a List<Object>.






                  share|improve this answer














                  No, a List<Dog> is not a List<Animal>. Consider what you can do with a List<Animal> - you can add any animal to it... including a cat. Now, can you logically add a cat to a litter of puppies? Absolutely not.



                  // Illegal code - because otherwise life would be Bad
                  List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
                  List<Animal> animals = dogs; // Awooga awooga
                  animals.add(new Cat());
                  Dog dog = dogs.get(0); // This should be safe, right?


                  Suddenly you have a very confused cat.



                  Now, you can't add a Cat to a List<? extends Animal> because you don't know it's a List<Cat>. You can retrieve a value and know that it will be an Animal, but you can't add arbitrary animals. The reverse is true for List<? super Animal> - in that case you can add an Animal to it safely, but you don't know anything about what might be retrieved from it, because it could be a List<Object>.







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Aug 25 '16 at 6:57

























                  answered Apr 30 '10 at 14:44









                  Jon Skeet

                  1069k67178348373




                  1069k67178348373








                  • 37




                    Interestingly, every list of dogs is indeed a list of animals, just like intuition tells us. The point is, that not every list of animals is a list of dogs, hence mutattion of the list by adding a cat is the problem.
                    – Ingo
                    Jan 28 '13 at 19:29






                  • 43




                    @Ingo: No, not really: you can add a cat to a list of animals, but you can't add a cat to a list of dogs. A list of dogs is only a list of animals if you consider it in a read-only sense.
                    – Jon Skeet
                    Jan 28 '13 at 19:33






                  • 9




                    @JonSkeet - Of course, but who is mandating that making a new list from a cat and a list of dogs actually changes the list of dogs? This is an arbitrary implementation decision in Java. One that goes counter to logic and intuition.
                    – Ingo
                    Jan 28 '13 at 19:41






                  • 6




                    @Ingo: I wouldn't have used that "certainly" to start with. If you have a list which says at the top "Hotels we might want to go to" and then someone added a swimming pool to it, would you think that valid? No - it's a list of hotels, which isn't a list of buildings. And it's not like I even said "A list of dogs is not a list of animals" - I put it in code terms, in a code font. I really don't think there's any ambiguity here. Using subclass would be incorrect anyway - it's about assignment compatibility, not subclassing.
                    – Jon Skeet
                    Jan 28 '13 at 19:58






                  • 11




                    @ruakh: The problem is that you're then punting to execution time something which can be blocked at compile-time. And I'd argue that array covariance was a design mistake to start with.
                    – Jon Skeet
                    Jul 3 '13 at 17:22














                  • 37




                    Interestingly, every list of dogs is indeed a list of animals, just like intuition tells us. The point is, that not every list of animals is a list of dogs, hence mutattion of the list by adding a cat is the problem.
                    – Ingo
                    Jan 28 '13 at 19:29






                  • 43




                    @Ingo: No, not really: you can add a cat to a list of animals, but you can't add a cat to a list of dogs. A list of dogs is only a list of animals if you consider it in a read-only sense.
                    – Jon Skeet
                    Jan 28 '13 at 19:33






                  • 9




                    @JonSkeet - Of course, but who is mandating that making a new list from a cat and a list of dogs actually changes the list of dogs? This is an arbitrary implementation decision in Java. One that goes counter to logic and intuition.
                    – Ingo
                    Jan 28 '13 at 19:41






                  • 6




                    @Ingo: I wouldn't have used that "certainly" to start with. If you have a list which says at the top "Hotels we might want to go to" and then someone added a swimming pool to it, would you think that valid? No - it's a list of hotels, which isn't a list of buildings. And it's not like I even said "A list of dogs is not a list of animals" - I put it in code terms, in a code font. I really don't think there's any ambiguity here. Using subclass would be incorrect anyway - it's about assignment compatibility, not subclassing.
                    – Jon Skeet
                    Jan 28 '13 at 19:58






                  • 11




                    @ruakh: The problem is that you're then punting to execution time something which can be blocked at compile-time. And I'd argue that array covariance was a design mistake to start with.
                    – Jon Skeet
                    Jul 3 '13 at 17:22








                  37




                  37




                  Interestingly, every list of dogs is indeed a list of animals, just like intuition tells us. The point is, that not every list of animals is a list of dogs, hence mutattion of the list by adding a cat is the problem.
                  – Ingo
                  Jan 28 '13 at 19:29




                  Interestingly, every list of dogs is indeed a list of animals, just like intuition tells us. The point is, that not every list of animals is a list of dogs, hence mutattion of the list by adding a cat is the problem.
                  – Ingo
                  Jan 28 '13 at 19:29




                  43




                  43




                  @Ingo: No, not really: you can add a cat to a list of animals, but you can't add a cat to a list of dogs. A list of dogs is only a list of animals if you consider it in a read-only sense.
                  – Jon Skeet
                  Jan 28 '13 at 19:33




                  @Ingo: No, not really: you can add a cat to a list of animals, but you can't add a cat to a list of dogs. A list of dogs is only a list of animals if you consider it in a read-only sense.
                  – Jon Skeet
                  Jan 28 '13 at 19:33




                  9




                  9




                  @JonSkeet - Of course, but who is mandating that making a new list from a cat and a list of dogs actually changes the list of dogs? This is an arbitrary implementation decision in Java. One that goes counter to logic and intuition.
                  – Ingo
                  Jan 28 '13 at 19:41




                  @JonSkeet - Of course, but who is mandating that making a new list from a cat and a list of dogs actually changes the list of dogs? This is an arbitrary implementation decision in Java. One that goes counter to logic and intuition.
                  – Ingo
                  Jan 28 '13 at 19:41




                  6




                  6




                  @Ingo: I wouldn't have used that "certainly" to start with. If you have a list which says at the top "Hotels we might want to go to" and then someone added a swimming pool to it, would you think that valid? No - it's a list of hotels, which isn't a list of buildings. And it's not like I even said "A list of dogs is not a list of animals" - I put it in code terms, in a code font. I really don't think there's any ambiguity here. Using subclass would be incorrect anyway - it's about assignment compatibility, not subclassing.
                  – Jon Skeet
                  Jan 28 '13 at 19:58




                  @Ingo: I wouldn't have used that "certainly" to start with. If you have a list which says at the top "Hotels we might want to go to" and then someone added a swimming pool to it, would you think that valid? No - it's a list of hotels, which isn't a list of buildings. And it's not like I even said "A list of dogs is not a list of animals" - I put it in code terms, in a code font. I really don't think there's any ambiguity here. Using subclass would be incorrect anyway - it's about assignment compatibility, not subclassing.
                  – Jon Skeet
                  Jan 28 '13 at 19:58




                  11




                  11




                  @ruakh: The problem is that you're then punting to execution time something which can be blocked at compile-time. And I'd argue that array covariance was a design mistake to start with.
                  – Jon Skeet
                  Jul 3 '13 at 17:22




                  @ruakh: The problem is that you're then punting to execution time something which can be blocked at compile-time. And I'd argue that array covariance was a design mistake to start with.
                  – Jon Skeet
                  Jul 3 '13 at 17:22












                  up vote
                  70
                  down vote













                  What you are looking for is called covariant type parameters. This means that if one type of object can be substituted for another in a method (for instance, Animal can be replaced with Dog), the same applies to expressions using those objects (so List<Animal> could be replaced with List<Dog>). The problem is that covariance is not safe for mutable lists in general. Suppose you have a List<Dog>, and it is being used as a List<Animal>. What happens when you try to add a Cat to this List<Animal> which is really a List<Dog>? Automatically allowing type parameters to be covariant breaks the type system.



                  It would be useful to add syntax to allow type parameters to be specified as covariant, which avoids the ? extends Foo in method declarations, but that does add additional complexity.






                  share|improve this answer























                  • Or use: List<Object>
                    – Mo'in Creemers
                    Aug 29 '12 at 11:35

















                  up vote
                  70
                  down vote













                  What you are looking for is called covariant type parameters. This means that if one type of object can be substituted for another in a method (for instance, Animal can be replaced with Dog), the same applies to expressions using those objects (so List<Animal> could be replaced with List<Dog>). The problem is that covariance is not safe for mutable lists in general. Suppose you have a List<Dog>, and it is being used as a List<Animal>. What happens when you try to add a Cat to this List<Animal> which is really a List<Dog>? Automatically allowing type parameters to be covariant breaks the type system.



                  It would be useful to add syntax to allow type parameters to be specified as covariant, which avoids the ? extends Foo in method declarations, but that does add additional complexity.






                  share|improve this answer























                  • Or use: List<Object>
                    – Mo'in Creemers
                    Aug 29 '12 at 11:35















                  up vote
                  70
                  down vote










                  up vote
                  70
                  down vote









                  What you are looking for is called covariant type parameters. This means that if one type of object can be substituted for another in a method (for instance, Animal can be replaced with Dog), the same applies to expressions using those objects (so List<Animal> could be replaced with List<Dog>). The problem is that covariance is not safe for mutable lists in general. Suppose you have a List<Dog>, and it is being used as a List<Animal>. What happens when you try to add a Cat to this List<Animal> which is really a List<Dog>? Automatically allowing type parameters to be covariant breaks the type system.



                  It would be useful to add syntax to allow type parameters to be specified as covariant, which avoids the ? extends Foo in method declarations, but that does add additional complexity.






                  share|improve this answer














                  What you are looking for is called covariant type parameters. This means that if one type of object can be substituted for another in a method (for instance, Animal can be replaced with Dog), the same applies to expressions using those objects (so List<Animal> could be replaced with List<Dog>). The problem is that covariance is not safe for mutable lists in general. Suppose you have a List<Dog>, and it is being used as a List<Animal>. What happens when you try to add a Cat to this List<Animal> which is really a List<Dog>? Automatically allowing type parameters to be covariant breaks the type system.



                  It would be useful to add syntax to allow type parameters to be specified as covariant, which avoids the ? extends Foo in method declarations, but that does add additional complexity.







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Nov 10 at 23:31









                  TechnicallyTrue

                  253




                  253










                  answered Apr 30 '10 at 14:44









                  Michael Ekstrand

                  20.3k75178




                  20.3k75178












                  • Or use: List<Object>
                    – Mo'in Creemers
                    Aug 29 '12 at 11:35




















                  • Or use: List<Object>
                    – Mo'in Creemers
                    Aug 29 '12 at 11:35


















                  Or use: List<Object>
                  – Mo'in Creemers
                  Aug 29 '12 at 11:35






                  Or use: List<Object>
                  – Mo'in Creemers
                  Aug 29 '12 at 11:35












                  up vote
                  42
                  down vote













                  The reason a List<Dog> is not a List<Animal>, is that, for example, you can insert a Cat into a List<Animal>, but not into a List<Dog>... you can use wildcards to make generics more extensible where possible; for example, reading from a List<Dog> is the similar to reading from a List<Animal> -- but not writing.



                  The Generics in the Java Language and the Section on Generics from the Java Tutorials have a very good, in-depth explanation as to why some things are or are not polymorphic or permitted with generics.






                  share|improve this answer



























                    up vote
                    42
                    down vote













                    The reason a List<Dog> is not a List<Animal>, is that, for example, you can insert a Cat into a List<Animal>, but not into a List<Dog>... you can use wildcards to make generics more extensible where possible; for example, reading from a List<Dog> is the similar to reading from a List<Animal> -- but not writing.



                    The Generics in the Java Language and the Section on Generics from the Java Tutorials have a very good, in-depth explanation as to why some things are or are not polymorphic or permitted with generics.






                    share|improve this answer

























                      up vote
                      42
                      down vote










                      up vote
                      42
                      down vote









                      The reason a List<Dog> is not a List<Animal>, is that, for example, you can insert a Cat into a List<Animal>, but not into a List<Dog>... you can use wildcards to make generics more extensible where possible; for example, reading from a List<Dog> is the similar to reading from a List<Animal> -- but not writing.



                      The Generics in the Java Language and the Section on Generics from the Java Tutorials have a very good, in-depth explanation as to why some things are or are not polymorphic or permitted with generics.






                      share|improve this answer














                      The reason a List<Dog> is not a List<Animal>, is that, for example, you can insert a Cat into a List<Animal>, but not into a List<Dog>... you can use wildcards to make generics more extensible where possible; for example, reading from a List<Dog> is the similar to reading from a List<Animal> -- but not writing.



                      The Generics in the Java Language and the Section on Generics from the Java Tutorials have a very good, in-depth explanation as to why some things are or are not polymorphic or permitted with generics.







                      share|improve this answer














                      share|improve this answer



                      share|improve this answer








                      edited Apr 18 '15 at 19:21









                      JonasCz

                      9,26052750




                      9,26052750










                      answered Apr 30 '10 at 14:46









                      Michael Aaron Safyan

                      75.7k13112182




                      75.7k13112182






















                          up vote
                          32
                          down vote













                          I would say the whole point of Generics is that it doesn't allow that. Consider the situation with arrays, which do allow that type of covariance:



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


                          That code compiles fine, but throws a runtime error (java.lang.ArrayStoreException: java.lang.Boolean in the second line). It is not typesafe. The point of Generics is to add the compile time type safety, otherwise you could just stick with a plain class without generics.



                          Now there are times where you need to be more flexible and that is what the ? super Class and ? extends Class are for. The former is when you need to insert into a type Collection (for example), and the latter is for when you need to read from it, in a type safe manner. But the only way to do both at the same time is to have a specific type.






                          share|improve this answer



















                          • 12




                            Arguably, array covariance is a language design bug. Note that due to type erasure, the same behaviour is technically impossible for generic collection.
                            – Michael Borgwardt
                            Apr 30 '10 at 14:55










                          • FYI: I mentioned your answer in stackoverflow.com/a/26551453/295802
                            – Mark Bennett
                            Oct 24 '14 at 17:16










                          • "I would say the whole point of Generics is that it doesn't allow that.". You can never be sure: Java and Scala's Type Systems are Unsound: The Existential Crisis of Null Pointers (presented at OOPSLA 2016) (since corrected it seems)
                            – David Tonhofer
                            Aug 15 '17 at 17:52















                          up vote
                          32
                          down vote













                          I would say the whole point of Generics is that it doesn't allow that. Consider the situation with arrays, which do allow that type of covariance:



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


                          That code compiles fine, but throws a runtime error (java.lang.ArrayStoreException: java.lang.Boolean in the second line). It is not typesafe. The point of Generics is to add the compile time type safety, otherwise you could just stick with a plain class without generics.



                          Now there are times where you need to be more flexible and that is what the ? super Class and ? extends Class are for. The former is when you need to insert into a type Collection (for example), and the latter is for when you need to read from it, in a type safe manner. But the only way to do both at the same time is to have a specific type.






                          share|improve this answer



















                          • 12




                            Arguably, array covariance is a language design bug. Note that due to type erasure, the same behaviour is technically impossible for generic collection.
                            – Michael Borgwardt
                            Apr 30 '10 at 14:55










                          • FYI: I mentioned your answer in stackoverflow.com/a/26551453/295802
                            – Mark Bennett
                            Oct 24 '14 at 17:16










                          • "I would say the whole point of Generics is that it doesn't allow that.". You can never be sure: Java and Scala's Type Systems are Unsound: The Existential Crisis of Null Pointers (presented at OOPSLA 2016) (since corrected it seems)
                            – David Tonhofer
                            Aug 15 '17 at 17:52













                          up vote
                          32
                          down vote










                          up vote
                          32
                          down vote









                          I would say the whole point of Generics is that it doesn't allow that. Consider the situation with arrays, which do allow that type of covariance:



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


                          That code compiles fine, but throws a runtime error (java.lang.ArrayStoreException: java.lang.Boolean in the second line). It is not typesafe. The point of Generics is to add the compile time type safety, otherwise you could just stick with a plain class without generics.



                          Now there are times where you need to be more flexible and that is what the ? super Class and ? extends Class are for. The former is when you need to insert into a type Collection (for example), and the latter is for when you need to read from it, in a type safe manner. But the only way to do both at the same time is to have a specific type.






                          share|improve this answer














                          I would say the whole point of Generics is that it doesn't allow that. Consider the situation with arrays, which do allow that type of covariance:



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


                          That code compiles fine, but throws a runtime error (java.lang.ArrayStoreException: java.lang.Boolean in the second line). It is not typesafe. The point of Generics is to add the compile time type safety, otherwise you could just stick with a plain class without generics.



                          Now there are times where you need to be more flexible and that is what the ? super Class and ? extends Class are for. The former is when you need to insert into a type Collection (for example), and the latter is for when you need to read from it, in a type safe manner. But the only way to do both at the same time is to have a specific type.







                          share|improve this answer














                          share|improve this answer



                          share|improve this answer








                          edited Aug 15 '17 at 17:33









                          David Tonhofer

                          5,27513231




                          5,27513231










                          answered Apr 30 '10 at 14:50









                          Yishai

                          71k20158237




                          71k20158237








                          • 12




                            Arguably, array covariance is a language design bug. Note that due to type erasure, the same behaviour is technically impossible for generic collection.
                            – Michael Borgwardt
                            Apr 30 '10 at 14:55










                          • FYI: I mentioned your answer in stackoverflow.com/a/26551453/295802
                            – Mark Bennett
                            Oct 24 '14 at 17:16










                          • "I would say the whole point of Generics is that it doesn't allow that.". You can never be sure: Java and Scala's Type Systems are Unsound: The Existential Crisis of Null Pointers (presented at OOPSLA 2016) (since corrected it seems)
                            – David Tonhofer
                            Aug 15 '17 at 17:52














                          • 12




                            Arguably, array covariance is a language design bug. Note that due to type erasure, the same behaviour is technically impossible for generic collection.
                            – Michael Borgwardt
                            Apr 30 '10 at 14:55










                          • FYI: I mentioned your answer in stackoverflow.com/a/26551453/295802
                            – Mark Bennett
                            Oct 24 '14 at 17:16










                          • "I would say the whole point of Generics is that it doesn't allow that.". You can never be sure: Java and Scala's Type Systems are Unsound: The Existential Crisis of Null Pointers (presented at OOPSLA 2016) (since corrected it seems)
                            – David Tonhofer
                            Aug 15 '17 at 17:52








                          12




                          12




                          Arguably, array covariance is a language design bug. Note that due to type erasure, the same behaviour is technically impossible for generic collection.
                          – Michael Borgwardt
                          Apr 30 '10 at 14:55




                          Arguably, array covariance is a language design bug. Note that due to type erasure, the same behaviour is technically impossible for generic collection.
                          – Michael Borgwardt
                          Apr 30 '10 at 14:55












                          FYI: I mentioned your answer in stackoverflow.com/a/26551453/295802
                          – Mark Bennett
                          Oct 24 '14 at 17:16




                          FYI: I mentioned your answer in stackoverflow.com/a/26551453/295802
                          – Mark Bennett
                          Oct 24 '14 at 17:16












                          "I would say the whole point of Generics is that it doesn't allow that.". You can never be sure: Java and Scala's Type Systems are Unsound: The Existential Crisis of Null Pointers (presented at OOPSLA 2016) (since corrected it seems)
                          – David Tonhofer
                          Aug 15 '17 at 17:52




                          "I would say the whole point of Generics is that it doesn't allow that.". You can never be sure: Java and Scala's Type Systems are Unsound: The Existential Crisis of Null Pointers (presented at OOPSLA 2016) (since corrected it seems)
                          – David Tonhofer
                          Aug 15 '17 at 17:52










                          up vote
                          28
                          down vote













                          A point I think should be added to what other answers mention is that while




                          List<Dog> isn't-a List<Animal> in Java




                          it is also true that




                          A list of dogs is-a list of animals in English (well, under a reasonable interpretation)




                          The way the OP's intuition works - which is completely valid of course - is the latter sentence. However, if we apply this intuition we get a language that is not Java-esque in its type system: Suppose our language does allow adding a cat to our list of dogs. What would that mean? It would mean that the list ceases to be a list of dogs, and remains merely a list of animals. And a list of mammals, and a list of quadrapeds.



                          To put it another way: A List<Dog> in Java does not mean "a list of dogs" in English, it means "a list which can have dogs, and nothing else".



                          More generally, OP's intuition lends itself towards a language in which operations on objects can change their type, or rather, an object's type(s) is a (dynamic) function of its value.






                          share|improve this answer























                          • Yes, human language is more fuzzy. But still, once you add a different animal to the list of dogs, it is still a list of animals, but no longer a list of dogs. The difference being, a human, with the fuzzy logic, usually has no problem realizing that.
                            – Vlasec
                            Nov 13 '17 at 12:14

















                          up vote
                          28
                          down vote













                          A point I think should be added to what other answers mention is that while




                          List<Dog> isn't-a List<Animal> in Java




                          it is also true that




                          A list of dogs is-a list of animals in English (well, under a reasonable interpretation)




                          The way the OP's intuition works - which is completely valid of course - is the latter sentence. However, if we apply this intuition we get a language that is not Java-esque in its type system: Suppose our language does allow adding a cat to our list of dogs. What would that mean? It would mean that the list ceases to be a list of dogs, and remains merely a list of animals. And a list of mammals, and a list of quadrapeds.



                          To put it another way: A List<Dog> in Java does not mean "a list of dogs" in English, it means "a list which can have dogs, and nothing else".



                          More generally, OP's intuition lends itself towards a language in which operations on objects can change their type, or rather, an object's type(s) is a (dynamic) function of its value.






                          share|improve this answer























                          • Yes, human language is more fuzzy. But still, once you add a different animal to the list of dogs, it is still a list of animals, but no longer a list of dogs. The difference being, a human, with the fuzzy logic, usually has no problem realizing that.
                            – Vlasec
                            Nov 13 '17 at 12:14















                          up vote
                          28
                          down vote










                          up vote
                          28
                          down vote









                          A point I think should be added to what other answers mention is that while




                          List<Dog> isn't-a List<Animal> in Java




                          it is also true that




                          A list of dogs is-a list of animals in English (well, under a reasonable interpretation)




                          The way the OP's intuition works - which is completely valid of course - is the latter sentence. However, if we apply this intuition we get a language that is not Java-esque in its type system: Suppose our language does allow adding a cat to our list of dogs. What would that mean? It would mean that the list ceases to be a list of dogs, and remains merely a list of animals. And a list of mammals, and a list of quadrapeds.



                          To put it another way: A List<Dog> in Java does not mean "a list of dogs" in English, it means "a list which can have dogs, and nothing else".



                          More generally, OP's intuition lends itself towards a language in which operations on objects can change their type, or rather, an object's type(s) is a (dynamic) function of its value.






                          share|improve this answer














                          A point I think should be added to what other answers mention is that while




                          List<Dog> isn't-a List<Animal> in Java




                          it is also true that




                          A list of dogs is-a list of animals in English (well, under a reasonable interpretation)




                          The way the OP's intuition works - which is completely valid of course - is the latter sentence. However, if we apply this intuition we get a language that is not Java-esque in its type system: Suppose our language does allow adding a cat to our list of dogs. What would that mean? It would mean that the list ceases to be a list of dogs, and remains merely a list of animals. And a list of mammals, and a list of quadrapeds.



                          To put it another way: A List<Dog> in Java does not mean "a list of dogs" in English, it means "a list which can have dogs, and nothing else".



                          More generally, OP's intuition lends itself towards a language in which operations on objects can change their type, or rather, an object's type(s) is a (dynamic) function of its value.







                          share|improve this answer














                          share|improve this answer



                          share|improve this answer








                          edited Sep 29 '17 at 14:47

























                          answered Mar 30 '13 at 7:14









                          einpoklum

                          32.5k26113230




                          32.5k26113230












                          • Yes, human language is more fuzzy. But still, once you add a different animal to the list of dogs, it is still a list of animals, but no longer a list of dogs. The difference being, a human, with the fuzzy logic, usually has no problem realizing that.
                            – Vlasec
                            Nov 13 '17 at 12:14




















                          • Yes, human language is more fuzzy. But still, once you add a different animal to the list of dogs, it is still a list of animals, but no longer a list of dogs. The difference being, a human, with the fuzzy logic, usually has no problem realizing that.
                            – Vlasec
                            Nov 13 '17 at 12:14


















                          Yes, human language is more fuzzy. But still, once you add a different animal to the list of dogs, it is still a list of animals, but no longer a list of dogs. The difference being, a human, with the fuzzy logic, usually has no problem realizing that.
                          – Vlasec
                          Nov 13 '17 at 12:14






                          Yes, human language is more fuzzy. But still, once you add a different animal to the list of dogs, it is still a list of animals, but no longer a list of dogs. The difference being, a human, with the fuzzy logic, usually has no problem realizing that.
                          – Vlasec
                          Nov 13 '17 at 12:14












                          up vote
                          7
                          down vote













                          To understand the problem it's useful to make comparison to arrays.



                          List<Dog> is not subclass of List<Animal>.
                          But Dog is subclass of Animal.



                          Arrays are reifiable and covariant.
                          Reifiable means their type information is fully available at runtime.
                          Therefore arrays provide runtime type safety but not compile-time type safety.



                              // 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


                          It's vice versa for generics:
                          Generics are erased and invariant.
                          Therefore generics can't provide runtime type safety, but they provide compile-time type safety.
                          In the code below if generics were covariant it will be possible to make heap pollution at line 3.



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





                          share|improve this answer



















                          • 2




                            It might be argued that, precisely because of that, Arrays in Java are broken,
                            – leonbloy
                            Dec 15 '17 at 14:41










                          • Arrays being covariant is a compiler "feature".
                            – Cristik
                            Mar 2 at 7:40















                          up vote
                          7
                          down vote













                          To understand the problem it's useful to make comparison to arrays.



                          List<Dog> is not subclass of List<Animal>.
                          But Dog is subclass of Animal.



                          Arrays are reifiable and covariant.
                          Reifiable means their type information is fully available at runtime.
                          Therefore arrays provide runtime type safety but not compile-time type safety.



                              // 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


                          It's vice versa for generics:
                          Generics are erased and invariant.
                          Therefore generics can't provide runtime type safety, but they provide compile-time type safety.
                          In the code below if generics were covariant it will be possible to make heap pollution at line 3.



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





                          share|improve this answer



















                          • 2




                            It might be argued that, precisely because of that, Arrays in Java are broken,
                            – leonbloy
                            Dec 15 '17 at 14:41










                          • Arrays being covariant is a compiler "feature".
                            – Cristik
                            Mar 2 at 7:40













                          up vote
                          7
                          down vote










                          up vote
                          7
                          down vote









                          To understand the problem it's useful to make comparison to arrays.



                          List<Dog> is not subclass of List<Animal>.
                          But Dog is subclass of Animal.



                          Arrays are reifiable and covariant.
                          Reifiable means their type information is fully available at runtime.
                          Therefore arrays provide runtime type safety but not compile-time type safety.



                              // 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


                          It's vice versa for generics:
                          Generics are erased and invariant.
                          Therefore generics can't provide runtime type safety, but they provide compile-time type safety.
                          In the code below if generics were covariant it will be possible to make heap pollution at line 3.



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





                          share|improve this answer














                          To understand the problem it's useful to make comparison to arrays.



                          List<Dog> is not subclass of List<Animal>.
                          But Dog is subclass of Animal.



                          Arrays are reifiable and covariant.
                          Reifiable means their type information is fully available at runtime.
                          Therefore arrays provide runtime type safety but not compile-time type safety.



                              // 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


                          It's vice versa for generics:
                          Generics are erased and invariant.
                          Therefore generics can't provide runtime type safety, but they provide compile-time type safety.
                          In the code below if generics were covariant it will be possible to make heap pollution at line 3.



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






                          share|improve this answer














                          share|improve this answer



                          share|improve this answer








                          edited Sep 29 '17 at 20:01

























                          answered Sep 29 '17 at 19:55









                          outdev

                          3,05321128




                          3,05321128








                          • 2




                            It might be argued that, precisely because of that, Arrays in Java are broken,
                            – leonbloy
                            Dec 15 '17 at 14:41










                          • Arrays being covariant is a compiler "feature".
                            – Cristik
                            Mar 2 at 7:40














                          • 2




                            It might be argued that, precisely because of that, Arrays in Java are broken,
                            – leonbloy
                            Dec 15 '17 at 14:41










                          • Arrays being covariant is a compiler "feature".
                            – Cristik
                            Mar 2 at 7:40








                          2




                          2




                          It might be argued that, precisely because of that, Arrays in Java are broken,
                          – leonbloy
                          Dec 15 '17 at 14:41




                          It might be argued that, precisely because of that, Arrays in Java are broken,
                          – leonbloy
                          Dec 15 '17 at 14:41












                          Arrays being covariant is a compiler "feature".
                          – Cristik
                          Mar 2 at 7:40




                          Arrays being covariant is a compiler "feature".
                          – Cristik
                          Mar 2 at 7:40










                          up vote
                          4
                          down vote













                          The answers given here didn't fully convince me. So instead, I make another example.



                          public void passOn(Consumer<Animal> consumer, Supplier<Animal> supplier) {
                          consumer.accept(supplier.get());
                          }


                          sounds fine, doesn't it? But you can only pass Consumers and Suppliers for Animals. If you have a Mammal consumer, but a Duck supplier, they should not fit although both are animals. In order to disallow this, additional restrictions have been added.



                          Instead of the above, we have to define relationships between the types we use.



                          E. g.,



                          public <A extends Animal> void passOn(Consumer<A> consumer, Supplier<? extends A> supplier) {
                          consumer.accept(supplier.get());
                          }


                          makes sure that we can only use a supplier which provides us the right type of object for the consumer.



                          OTOH, we could as well do



                          public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<A> supplier) {
                          consumer.accept(supplier.get());
                          }


                          where we go the other way: we define the type of the Supplier and restrict that it can be put into the Consumer.



                          We even can do



                          public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<? extends A> supplier) {
                          consumer.accept(supplier.get());
                          }


                          where, having the intuitive relations Life -> Animal -> Mammal -> Dog, Cat etc., we could even put a Mammal into a Life consumer, but not a String into a Life consumer.






                          share|improve this answer

















                          • 1




                            Among the 4 versions, #2 is probably incorrect. e.g. we cannot call it with (Consumer<Runnable>, Supplier<Dog>) while Dog is subtype of Animal & Runnable
                            – ZhongYu
                            Jul 27 '15 at 20:10












                          • groups.google.com/forum/#!topic/java-lang-fans/0GDv0salPTs
                            – ZhongYu
                            Jul 27 '15 at 20:39















                          up vote
                          4
                          down vote













                          The answers given here didn't fully convince me. So instead, I make another example.



                          public void passOn(Consumer<Animal> consumer, Supplier<Animal> supplier) {
                          consumer.accept(supplier.get());
                          }


                          sounds fine, doesn't it? But you can only pass Consumers and Suppliers for Animals. If you have a Mammal consumer, but a Duck supplier, they should not fit although both are animals. In order to disallow this, additional restrictions have been added.



                          Instead of the above, we have to define relationships between the types we use.



                          E. g.,



                          public <A extends Animal> void passOn(Consumer<A> consumer, Supplier<? extends A> supplier) {
                          consumer.accept(supplier.get());
                          }


                          makes sure that we can only use a supplier which provides us the right type of object for the consumer.



                          OTOH, we could as well do



                          public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<A> supplier) {
                          consumer.accept(supplier.get());
                          }


                          where we go the other way: we define the type of the Supplier and restrict that it can be put into the Consumer.



                          We even can do



                          public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<? extends A> supplier) {
                          consumer.accept(supplier.get());
                          }


                          where, having the intuitive relations Life -> Animal -> Mammal -> Dog, Cat etc., we could even put a Mammal into a Life consumer, but not a String into a Life consumer.






                          share|improve this answer

















                          • 1




                            Among the 4 versions, #2 is probably incorrect. e.g. we cannot call it with (Consumer<Runnable>, Supplier<Dog>) while Dog is subtype of Animal & Runnable
                            – ZhongYu
                            Jul 27 '15 at 20:10












                          • groups.google.com/forum/#!topic/java-lang-fans/0GDv0salPTs
                            – ZhongYu
                            Jul 27 '15 at 20:39













                          up vote
                          4
                          down vote










                          up vote
                          4
                          down vote









                          The answers given here didn't fully convince me. So instead, I make another example.



                          public void passOn(Consumer<Animal> consumer, Supplier<Animal> supplier) {
                          consumer.accept(supplier.get());
                          }


                          sounds fine, doesn't it? But you can only pass Consumers and Suppliers for Animals. If you have a Mammal consumer, but a Duck supplier, they should not fit although both are animals. In order to disallow this, additional restrictions have been added.



                          Instead of the above, we have to define relationships between the types we use.



                          E. g.,



                          public <A extends Animal> void passOn(Consumer<A> consumer, Supplier<? extends A> supplier) {
                          consumer.accept(supplier.get());
                          }


                          makes sure that we can only use a supplier which provides us the right type of object for the consumer.



                          OTOH, we could as well do



                          public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<A> supplier) {
                          consumer.accept(supplier.get());
                          }


                          where we go the other way: we define the type of the Supplier and restrict that it can be put into the Consumer.



                          We even can do



                          public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<? extends A> supplier) {
                          consumer.accept(supplier.get());
                          }


                          where, having the intuitive relations Life -> Animal -> Mammal -> Dog, Cat etc., we could even put a Mammal into a Life consumer, but not a String into a Life consumer.






                          share|improve this answer












                          The answers given here didn't fully convince me. So instead, I make another example.



                          public void passOn(Consumer<Animal> consumer, Supplier<Animal> supplier) {
                          consumer.accept(supplier.get());
                          }


                          sounds fine, doesn't it? But you can only pass Consumers and Suppliers for Animals. If you have a Mammal consumer, but a Duck supplier, they should not fit although both are animals. In order to disallow this, additional restrictions have been added.



                          Instead of the above, we have to define relationships between the types we use.



                          E. g.,



                          public <A extends Animal> void passOn(Consumer<A> consumer, Supplier<? extends A> supplier) {
                          consumer.accept(supplier.get());
                          }


                          makes sure that we can only use a supplier which provides us the right type of object for the consumer.



                          OTOH, we could as well do



                          public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<A> supplier) {
                          consumer.accept(supplier.get());
                          }


                          where we go the other way: we define the type of the Supplier and restrict that it can be put into the Consumer.



                          We even can do



                          public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<? extends A> supplier) {
                          consumer.accept(supplier.get());
                          }


                          where, having the intuitive relations Life -> Animal -> Mammal -> Dog, Cat etc., we could even put a Mammal into a Life consumer, but not a String into a Life consumer.







                          share|improve this answer












                          share|improve this answer



                          share|improve this answer










                          answered Feb 14 '15 at 21:26









                          glglgl

                          65k788163




                          65k788163








                          • 1




                            Among the 4 versions, #2 is probably incorrect. e.g. we cannot call it with (Consumer<Runnable>, Supplier<Dog>) while Dog is subtype of Animal & Runnable
                            – ZhongYu
                            Jul 27 '15 at 20:10












                          • groups.google.com/forum/#!topic/java-lang-fans/0GDv0salPTs
                            – ZhongYu
                            Jul 27 '15 at 20:39














                          • 1




                            Among the 4 versions, #2 is probably incorrect. e.g. we cannot call it with (Consumer<Runnable>, Supplier<Dog>) while Dog is subtype of Animal & Runnable
                            – ZhongYu
                            Jul 27 '15 at 20:10












                          • groups.google.com/forum/#!topic/java-lang-fans/0GDv0salPTs
                            – ZhongYu
                            Jul 27 '15 at 20:39








                          1




                          1




                          Among the 4 versions, #2 is probably incorrect. e.g. we cannot call it with (Consumer<Runnable>, Supplier<Dog>) while Dog is subtype of Animal & Runnable
                          – ZhongYu
                          Jul 27 '15 at 20:10






                          Among the 4 versions, #2 is probably incorrect. e.g. we cannot call it with (Consumer<Runnable>, Supplier<Dog>) while Dog is subtype of Animal & Runnable
                          – ZhongYu
                          Jul 27 '15 at 20:10














                          groups.google.com/forum/#!topic/java-lang-fans/0GDv0salPTs
                          – ZhongYu
                          Jul 27 '15 at 20:39




                          groups.google.com/forum/#!topic/java-lang-fans/0GDv0salPTs
                          – ZhongYu
                          Jul 27 '15 at 20:39










                          up vote
                          4
                          down vote













                          The basis logic for such behavior is that Generics follow a mechanism of type erasure. So at run time you have no way if identifying the type of collection unlike arrays where there is no such erasure process. So coming back to your question...



                          So suppose there is a method as given below:



                          add(List<Animal>){
                          //You can add List<Dog or List<Cat> and this will compile as per rules of polymorphism
                          }


                          Now if java allows caller to add List of type Animal to this method then you might add wrong thing into collection and at run time too it will run due to type erasure. While in case of arrays you will get a run time exception for such scenarios...



                          Thus in essence this behavior is implemented so that one cannot add wrong thing into collection. Now I believe type erasure exists so as to give compatibility with legacy java without generics....






                          share|improve this answer



























                            up vote
                            4
                            down vote













                            The basis logic for such behavior is that Generics follow a mechanism of type erasure. So at run time you have no way if identifying the type of collection unlike arrays where there is no such erasure process. So coming back to your question...



                            So suppose there is a method as given below:



                            add(List<Animal>){
                            //You can add List<Dog or List<Cat> and this will compile as per rules of polymorphism
                            }


                            Now if java allows caller to add List of type Animal to this method then you might add wrong thing into collection and at run time too it will run due to type erasure. While in case of arrays you will get a run time exception for such scenarios...



                            Thus in essence this behavior is implemented so that one cannot add wrong thing into collection. Now I believe type erasure exists so as to give compatibility with legacy java without generics....






                            share|improve this answer

























                              up vote
                              4
                              down vote










                              up vote
                              4
                              down vote









                              The basis logic for such behavior is that Generics follow a mechanism of type erasure. So at run time you have no way if identifying the type of collection unlike arrays where there is no such erasure process. So coming back to your question...



                              So suppose there is a method as given below:



                              add(List<Animal>){
                              //You can add List<Dog or List<Cat> and this will compile as per rules of polymorphism
                              }


                              Now if java allows caller to add List of type Animal to this method then you might add wrong thing into collection and at run time too it will run due to type erasure. While in case of arrays you will get a run time exception for such scenarios...



                              Thus in essence this behavior is implemented so that one cannot add wrong thing into collection. Now I believe type erasure exists so as to give compatibility with legacy java without generics....






                              share|improve this answer














                              The basis logic for such behavior is that Generics follow a mechanism of type erasure. So at run time you have no way if identifying the type of collection unlike arrays where there is no such erasure process. So coming back to your question...



                              So suppose there is a method as given below:



                              add(List<Animal>){
                              //You can add List<Dog or List<Cat> and this will compile as per rules of polymorphism
                              }


                              Now if java allows caller to add List of type Animal to this method then you might add wrong thing into collection and at run time too it will run due to type erasure. While in case of arrays you will get a run time exception for such scenarios...



                              Thus in essence this behavior is implemented so that one cannot add wrong thing into collection. Now I believe type erasure exists so as to give compatibility with legacy java without generics....







                              share|improve this answer














                              share|improve this answer



                              share|improve this answer








                              edited Jul 3 at 8:34









                              STaefi

                              3,33011535




                              3,33011535










                              answered Dec 4 '12 at 10:43









                              Hitesh

                              3701412




                              3701412






















                                  up vote
                                  3
                                  down vote













                                  Actually you can use an interface to achieve what you want.



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


                                  }



                                  you can then use the collections using



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





                                  share|improve this answer

























                                    up vote
                                    3
                                    down vote













                                    Actually you can use an interface to achieve what you want.



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


                                    }



                                    you can then use the collections using



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





                                    share|improve this answer























                                      up vote
                                      3
                                      down vote










                                      up vote
                                      3
                                      down vote









                                      Actually you can use an interface to achieve what you want.



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


                                      }



                                      you can then use the collections using



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





                                      share|improve this answer












                                      Actually you can use an interface to achieve what you want.



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


                                      }



                                      you can then use the collections using



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






                                      share|improve this answer












                                      share|improve this answer



                                      share|improve this answer










                                      answered Jul 12 '15 at 4:14









                                      Angel Koh

                                      5,72442754




                                      5,72442754






















                                          up vote
                                          1
                                          down vote













                                          If you are sure that the list items are subclasses of that given super type you can cast the list using this approach:



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


                                          This is usefull when you want to pass the list in a constructor or iterate over it






                                          share|improve this answer

















                                          • 2




                                            This will create more problems than it actually solves
                                            – Ferrybig
                                            Feb 1 '16 at 15:33










                                          • If you try to add a Cat to the list, sure it will create problems, but for looping purposes i think its the only non verbose answer.
                                            – sagits
                                            Feb 2 '16 at 12:28















                                          up vote
                                          1
                                          down vote













                                          If you are sure that the list items are subclasses of that given super type you can cast the list using this approach:



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


                                          This is usefull when you want to pass the list in a constructor or iterate over it






                                          share|improve this answer

















                                          • 2




                                            This will create more problems than it actually solves
                                            – Ferrybig
                                            Feb 1 '16 at 15:33










                                          • If you try to add a Cat to the list, sure it will create problems, but for looping purposes i think its the only non verbose answer.
                                            – sagits
                                            Feb 2 '16 at 12:28













                                          up vote
                                          1
                                          down vote










                                          up vote
                                          1
                                          down vote









                                          If you are sure that the list items are subclasses of that given super type you can cast the list using this approach:



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


                                          This is usefull when you want to pass the list in a constructor or iterate over it






                                          share|improve this answer












                                          If you are sure that the list items are subclasses of that given super type you can cast the list using this approach:



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


                                          This is usefull when you want to pass the list in a constructor or iterate over it







                                          share|improve this answer












                                          share|improve this answer



                                          share|improve this answer










                                          answered Jan 28 '16 at 21:11









                                          sagits

                                          3,1392535




                                          3,1392535








                                          • 2




                                            This will create more problems than it actually solves
                                            – Ferrybig
                                            Feb 1 '16 at 15:33










                                          • If you try to add a Cat to the list, sure it will create problems, but for looping purposes i think its the only non verbose answer.
                                            – sagits
                                            Feb 2 '16 at 12:28














                                          • 2




                                            This will create more problems than it actually solves
                                            – Ferrybig
                                            Feb 1 '16 at 15:33










                                          • If you try to add a Cat to the list, sure it will create problems, but for looping purposes i think its the only non verbose answer.
                                            – sagits
                                            Feb 2 '16 at 12:28








                                          2




                                          2




                                          This will create more problems than it actually solves
                                          – Ferrybig
                                          Feb 1 '16 at 15:33




                                          This will create more problems than it actually solves
                                          – Ferrybig
                                          Feb 1 '16 at 15:33












                                          If you try to add a Cat to the list, sure it will create problems, but for looping purposes i think its the only non verbose answer.
                                          – sagits
                                          Feb 2 '16 at 12:28




                                          If you try to add a Cat to the list, sure it will create problems, but for looping purposes i think its the only non verbose answer.
                                          – sagits
                                          Feb 2 '16 at 12:28










                                          up vote
                                          1
                                          down vote













                                          The answer as well as other answers are correct. I am going to add to those answers with a solution that I think will be helpful. I think this comes up often in programming. One thing to note is that for Collections (Lists, Sets, etc.) the main issue is adding to the Collection. That is where things break down. Even removing is OK.



                                          In most cases, we can use Collection<? extends T> rather then Collection<T> and that should be the first choice. However, I am finding cases where it is not easy to do that. It is up for debate as to whether that is always the best thing to do. I am presenting here a class DownCastCollection that can take convert a Collection<? extends T> to a Collection<T> (we can define similar classes for List, Set, NavigableSet,..) to be used when using the standard approach is very inconvenient. Below is an example of how to use it (we could also use Collection<? extends Object> in this case, but I am keeping it simple to illustrate using DownCastCollection.



                                          /**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));
                                          }


                                          Now the class:



                                          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();

                                          }


                                          }






                                          share|improve this answer























                                          • This is a good idea, so much so that it exists in Java SE already. ; ) Collections.unmodifiableCollection
                                            – Radiodef
                                            May 8 '15 at 21:30






                                          • 1




                                            Right but the collection I define can be modified.
                                            – dan b
                                            May 9 '15 at 11:40










                                          • Yes, it can be modified. Collection<? extends E> already handles that behavior correctly though, unless you use it in a way that is not type-safe (e.g. casting it to something else). The only advantage I see there is, when you call the add operation, it throws an exception even if you casted it.
                                            – Vlasec
                                            Nov 13 '17 at 12:47















                                          up vote
                                          1
                                          down vote













                                          The answer as well as other answers are correct. I am going to add to those answers with a solution that I think will be helpful. I think this comes up often in programming. One thing to note is that for Collections (Lists, Sets, etc.) the main issue is adding to the Collection. That is where things break down. Even removing is OK.



                                          In most cases, we can use Collection<? extends T> rather then Collection<T> and that should be the first choice. However, I am finding cases where it is not easy to do that. It is up for debate as to whether that is always the best thing to do. I am presenting here a class DownCastCollection that can take convert a Collection<? extends T> to a Collection<T> (we can define similar classes for List, Set, NavigableSet,..) to be used when using the standard approach is very inconvenient. Below is an example of how to use it (we could also use Collection<? extends Object> in this case, but I am keeping it simple to illustrate using DownCastCollection.



                                          /**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));
                                          }


                                          Now the class:



                                          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();

                                          }


                                          }






                                          share|improve this answer























                                          • This is a good idea, so much so that it exists in Java SE already. ; ) Collections.unmodifiableCollection
                                            – Radiodef
                                            May 8 '15 at 21:30






                                          • 1




                                            Right but the collection I define can be modified.
                                            – dan b
                                            May 9 '15 at 11:40










                                          • Yes, it can be modified. Collection<? extends E> already handles that behavior correctly though, unless you use it in a way that is not type-safe (e.g. casting it to something else). The only advantage I see there is, when you call the add operation, it throws an exception even if you casted it.
                                            – Vlasec
                                            Nov 13 '17 at 12:47













                                          up vote
                                          1
                                          down vote










                                          up vote
                                          1
                                          down vote









                                          The answer as well as other answers are correct. I am going to add to those answers with a solution that I think will be helpful. I think this comes up often in programming. One thing to note is that for Collections (Lists, Sets, etc.) the main issue is adding to the Collection. That is where things break down. Even removing is OK.



                                          In most cases, we can use Collection<? extends T> rather then Collection<T> and that should be the first choice. However, I am finding cases where it is not easy to do that. It is up for debate as to whether that is always the best thing to do. I am presenting here a class DownCastCollection that can take convert a Collection<? extends T> to a Collection<T> (we can define similar classes for List, Set, NavigableSet,..) to be used when using the standard approach is very inconvenient. Below is an example of how to use it (we could also use Collection<? extends Object> in this case, but I am keeping it simple to illustrate using DownCastCollection.



                                          /**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));
                                          }


                                          Now the class:



                                          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();

                                          }


                                          }






                                          share|improve this answer














                                          The answer as well as other answers are correct. I am going to add to those answers with a solution that I think will be helpful. I think this comes up often in programming. One thing to note is that for Collections (Lists, Sets, etc.) the main issue is adding to the Collection. That is where things break down. Even removing is OK.



                                          In most cases, we can use Collection<? extends T> rather then Collection<T> and that should be the first choice. However, I am finding cases where it is not easy to do that. It is up for debate as to whether that is always the best thing to do. I am presenting here a class DownCastCollection that can take convert a Collection<? extends T> to a Collection<T> (we can define similar classes for List, Set, NavigableSet,..) to be used when using the standard approach is very inconvenient. Below is an example of how to use it (we could also use Collection<? extends Object> in this case, but I am keeping it simple to illustrate using DownCastCollection.



                                          /**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));
                                          }


                                          Now the class:



                                          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();

                                          }


                                          }







                                          share|improve this answer














                                          share|improve this answer



                                          share|improve this answer








                                          edited Nov 7 '17 at 9:13









                                          Rudy Velthuis

                                          23.8k43474




                                          23.8k43474










                                          answered Dec 15 '14 at 11:14









                                          dan b

                                          1,017514




                                          1,017514












                                          • This is a good idea, so much so that it exists in Java SE already. ; ) Collections.unmodifiableCollection
                                            – Radiodef
                                            May 8 '15 at 21:30






                                          • 1




                                            Right but the collection I define can be modified.
                                            – dan b
                                            May 9 '15 at 11:40










                                          • Yes, it can be modified. Collection<? extends E> already handles that behavior correctly though, unless you use it in a way that is not type-safe (e.g. casting it to something else). The only advantage I see there is, when you call the add operation, it throws an exception even if you casted it.
                                            – Vlasec
                                            Nov 13 '17 at 12:47


















                                          • This is a good idea, so much so that it exists in Java SE already. ; ) Collections.unmodifiableCollection
                                            – Radiodef
                                            May 8 '15 at 21:30






                                          • 1




                                            Right but the collection I define can be modified.
                                            – dan b
                                            May 9 '15 at 11:40










                                          • Yes, it can be modified. Collection<? extends E> already handles that behavior correctly though, unless you use it in a way that is not type-safe (e.g. casting it to something else). The only advantage I see there is, when you call the add operation, it throws an exception even if you casted it.
                                            – Vlasec
                                            Nov 13 '17 at 12:47
















                                          This is a good idea, so much so that it exists in Java SE already. ; ) Collections.unmodifiableCollection
                                          – Radiodef
                                          May 8 '15 at 21:30




                                          This is a good idea, so much so that it exists in Java SE already. ; ) Collections.unmodifiableCollection
                                          – Radiodef
                                          May 8 '15 at 21:30




                                          1




                                          1




                                          Right but the collection I define can be modified.
                                          – dan b
                                          May 9 '15 at 11:40




                                          Right but the collection I define can be modified.
                                          – dan b
                                          May 9 '15 at 11:40












                                          Yes, it can be modified. Collection<? extends E> already handles that behavior correctly though, unless you use it in a way that is not type-safe (e.g. casting it to something else). The only advantage I see there is, when you call the add operation, it throws an exception even if you casted it.
                                          – Vlasec
                                          Nov 13 '17 at 12:47




                                          Yes, it can be modified. Collection<? extends E> already handles that behavior correctly though, unless you use it in a way that is not type-safe (e.g. casting it to something else). The only advantage I see there is, when you call the add operation, it throws an exception even if you casted it.
                                          – Vlasec
                                          Nov 13 '17 at 12:47










                                          up vote
                                          1
                                          down vote













                                          Subtyping is invariant for parameterized types. Even tough the class Dog is a subtype of Animal, the parameterized type List<Dog> is not a subtype of List<Animal>. In contrast, covariant subtyping is used by arrays, so the array
                                          type Dog is a subtype of Animal.



                                          Invariant subtyping ensures that the type constraints enforced by Java are not violated. Consider the following code given by @Jon Skeet:



                                          List<Dog> dogs = new ArrayList<Dog>(1);
                                          List<Animal> animals = dogs;
                                          animals.add(new Cat()); // compile-time error
                                          Dog dog = dogs.get(0);


                                          As stated by @Jon Skeet, this code is illegal, because otherwise it would violate the type constraints by returning a cat when a dog expected.



                                          It is instructive to compare the above to analogous code for arrays.



                                          Dog dogs = new Dog[1];
                                          Object animals = dogs;
                                          animals[0] = new Cat(); // run-time error
                                          Dog dog = dogs[0];


                                          The code is legal. However, throws an array store exception.
                                          An array carries its type at run-time this way JVM can enforce
                                          type safety of covariant subtyping.



                                          To understand this further let's look at the bytecode generated by javap of the class below:



                                          import java.util.ArrayList;
                                          import java.util.List;

                                          public class Demonstration {
                                          public void normal() {
                                          List normal = new ArrayList(1);
                                          normal.add("lorem ipsum");
                                          }

                                          public void parameterized() {
                                          List<String> parameterized = new ArrayList<>(1);
                                          parameterized.add("lorem ipsum");
                                          }
                                          }


                                          Using the command javap -c Demonstration, this shows the following Java bytecode:



                                          Compiled from "Demonstration.java"
                                          public class Demonstration {
                                          public Demonstration();
                                          Code:
                                          0: aload_0
                                          1: invokespecial #1 // Method java/lang/Object."<init>":()V
                                          4: return

                                          public void normal();
                                          Code:
                                          0: new #2 // class java/util/ArrayList
                                          3: dup
                                          4: iconst_1
                                          5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
                                          8: astore_1
                                          9: aload_1
                                          10: ldc #4 // String lorem ipsum
                                          12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
                                          17: pop
                                          18: return

                                          public void parameterized();
                                          Code:
                                          0: new #2 // class java/util/ArrayList
                                          3: dup
                                          4: iconst_1
                                          5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
                                          8: astore_1
                                          9: aload_1
                                          10: ldc #4 // String lorem ipsum
                                          12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
                                          17: pop
                                          18: return
                                          }


                                          Observe that the translated code of method bodies are identical. Compiler replaced each parameterized type by its erasure. This property is crucial meaning that it did not break backwards compatibility.



                                          In conclusion, run-time safety is not possible for parameterized types, since compiler replaces each parameterized type by its erasure. This makes parameterized types are nothing more than syntactic sugar.






                                          share|improve this answer

























                                            up vote
                                            1
                                            down vote













                                            Subtyping is invariant for parameterized types. Even tough the class Dog is a subtype of Animal, the parameterized type List<Dog> is not a subtype of List<Animal>. In contrast, covariant subtyping is used by arrays, so the array
                                            type Dog is a subtype of Animal.



                                            Invariant subtyping ensures that the type constraints enforced by Java are not violated. Consider the following code given by @Jon Skeet:



                                            List<Dog> dogs = new ArrayList<Dog>(1);
                                            List<Animal> animals = dogs;
                                            animals.add(new Cat()); // compile-time error
                                            Dog dog = dogs.get(0);


                                            As stated by @Jon Skeet, this code is illegal, because otherwise it would violate the type constraints by returning a cat when a dog expected.



                                            It is instructive to compare the above to analogous code for arrays.



                                            Dog dogs = new Dog[1];
                                            Object animals = dogs;
                                            animals[0] = new Cat(); // run-time error
                                            Dog dog = dogs[0];


                                            The code is legal. However, throws an array store exception.
                                            An array carries its type at run-time this way JVM can enforce
                                            type safety of covariant subtyping.



                                            To understand this further let's look at the bytecode generated by javap of the class below:



                                            import java.util.ArrayList;
                                            import java.util.List;

                                            public class Demonstration {
                                            public void normal() {
                                            List normal = new ArrayList(1);
                                            normal.add("lorem ipsum");
                                            }

                                            public void parameterized() {
                                            List<String> parameterized = new ArrayList<>(1);
                                            parameterized.add("lorem ipsum");
                                            }
                                            }


                                            Using the command javap -c Demonstration, this shows the following Java bytecode:



                                            Compiled from "Demonstration.java"
                                            public class Demonstration {
                                            public Demonstration();
                                            Code:
                                            0: aload_0
                                            1: invokespecial #1 // Method java/lang/Object."<init>":()V
                                            4: return

                                            public void normal();
                                            Code:
                                            0: new #2 // class java/util/ArrayList
                                            3: dup
                                            4: iconst_1
                                            5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
                                            8: astore_1
                                            9: aload_1
                                            10: ldc #4 // String lorem ipsum
                                            12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
                                            17: pop
                                            18: return

                                            public void parameterized();
                                            Code:
                                            0: new #2 // class java/util/ArrayList
                                            3: dup
                                            4: iconst_1
                                            5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
                                            8: astore_1
                                            9: aload_1
                                            10: ldc #4 // String lorem ipsum
                                            12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
                                            17: pop
                                            18: return
                                            }


                                            Observe that the translated code of method bodies are identical. Compiler replaced each parameterized type by its erasure. This property is crucial meaning that it did not break backwards compatibility.



                                            In conclusion, run-time safety is not possible for parameterized types, since compiler replaces each parameterized type by its erasure. This makes parameterized types are nothing more than syntactic sugar.






                                            share|improve this answer























                                              up vote
                                              1
                                              down vote










                                              up vote
                                              1
                                              down vote









                                              Subtyping is invariant for parameterized types. Even tough the class Dog is a subtype of Animal, the parameterized type List<Dog> is not a subtype of List<Animal>. In contrast, covariant subtyping is used by arrays, so the array
                                              type Dog is a subtype of Animal.



                                              Invariant subtyping ensures that the type constraints enforced by Java are not violated. Consider the following code given by @Jon Skeet:



                                              List<Dog> dogs = new ArrayList<Dog>(1);
                                              List<Animal> animals = dogs;
                                              animals.add(new Cat()); // compile-time error
                                              Dog dog = dogs.get(0);


                                              As stated by @Jon Skeet, this code is illegal, because otherwise it would violate the type constraints by returning a cat when a dog expected.



                                              It is instructive to compare the above to analogous code for arrays.



                                              Dog dogs = new Dog[1];
                                              Object animals = dogs;
                                              animals[0] = new Cat(); // run-time error
                                              Dog dog = dogs[0];


                                              The code is legal. However, throws an array store exception.
                                              An array carries its type at run-time this way JVM can enforce
                                              type safety of covariant subtyping.



                                              To understand this further let's look at the bytecode generated by javap of the class below:



                                              import java.util.ArrayList;
                                              import java.util.List;

                                              public class Demonstration {
                                              public void normal() {
                                              List normal = new ArrayList(1);
                                              normal.add("lorem ipsum");
                                              }

                                              public void parameterized() {
                                              List<String> parameterized = new ArrayList<>(1);
                                              parameterized.add("lorem ipsum");
                                              }
                                              }


                                              Using the command javap -c Demonstration, this shows the following Java bytecode:



                                              Compiled from "Demonstration.java"
                                              public class Demonstration {
                                              public Demonstration();
                                              Code:
                                              0: aload_0
                                              1: invokespecial #1 // Method java/lang/Object."<init>":()V
                                              4: return

                                              public void normal();
                                              Code:
                                              0: new #2 // class java/util/ArrayList
                                              3: dup
                                              4: iconst_1
                                              5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
                                              8: astore_1
                                              9: aload_1
                                              10: ldc #4 // String lorem ipsum
                                              12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
                                              17: pop
                                              18: return

                                              public void parameterized();
                                              Code:
                                              0: new #2 // class java/util/ArrayList
                                              3: dup
                                              4: iconst_1
                                              5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
                                              8: astore_1
                                              9: aload_1
                                              10: ldc #4 // String lorem ipsum
                                              12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
                                              17: pop
                                              18: return
                                              }


                                              Observe that the translated code of method bodies are identical. Compiler replaced each parameterized type by its erasure. This property is crucial meaning that it did not break backwards compatibility.



                                              In conclusion, run-time safety is not possible for parameterized types, since compiler replaces each parameterized type by its erasure. This makes parameterized types are nothing more than syntactic sugar.






                                              share|improve this answer












                                              Subtyping is invariant for parameterized types. Even tough the class Dog is a subtype of Animal, the parameterized type List<Dog> is not a subtype of List<Animal>. In contrast, covariant subtyping is used by arrays, so the array
                                              type Dog is a subtype of Animal.



                                              Invariant subtyping ensures that the type constraints enforced by Java are not violated. Consider the following code given by @Jon Skeet:



                                              List<Dog> dogs = new ArrayList<Dog>(1);
                                              List<Animal> animals = dogs;
                                              animals.add(new Cat()); // compile-time error
                                              Dog dog = dogs.get(0);


                                              As stated by @Jon Skeet, this code is illegal, because otherwise it would violate the type constraints by returning a cat when a dog expected.



                                              It is instructive to compare the above to analogous code for arrays.



                                              Dog dogs = new Dog[1];
                                              Object animals = dogs;
                                              animals[0] = new Cat(); // run-time error
                                              Dog dog = dogs[0];


                                              The code is legal. However, throws an array store exception.
                                              An array carries its type at run-time this way JVM can enforce
                                              type safety of covariant subtyping.



                                              To understand this further let's look at the bytecode generated by javap of the class below:



                                              import java.util.ArrayList;
                                              import java.util.List;

                                              public class Demonstration {
                                              public void normal() {
                                              List normal = new ArrayList(1);
                                              normal.add("lorem ipsum");
                                              }

                                              public void parameterized() {
                                              List<String> parameterized = new ArrayList<>(1);
                                              parameterized.add("lorem ipsum");
                                              }
                                              }


                                              Using the command javap -c Demonstration, this shows the following Java bytecode:



                                              Compiled from "Demonstration.java"
                                              public class Demonstration {
                                              public Demonstration();
                                              Code:
                                              0: aload_0
                                              1: invokespecial #1 // Method java/lang/Object."<init>":()V
                                              4: return

                                              public void normal();
                                              Code:
                                              0: new #2 // class java/util/ArrayList
                                              3: dup
                                              4: iconst_1
                                              5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
                                              8: astore_1
                                              9: aload_1
                                              10: ldc #4 // String lorem ipsum
                                              12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
                                              17: pop
                                              18: return

                                              public void parameterized();
                                              Code:
                                              0: new #2 // class java/util/ArrayList
                                              3: dup
                                              4: iconst_1
                                              5: invokespecial #3 // Method java/util/ArrayList."<init>":(I)V
                                              8: astore_1
                                              9: aload_1
                                              10: ldc #4 // String lorem ipsum
                                              12: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
                                              17: pop
                                              18: return
                                              }


                                              Observe that the translated code of method bodies are identical. Compiler replaced each parameterized type by its erasure. This property is crucial meaning that it did not break backwards compatibility.



                                              In conclusion, run-time safety is not possible for parameterized types, since compiler replaces each parameterized type by its erasure. This makes parameterized types are nothing more than syntactic sugar.







                                              share|improve this answer












                                              share|improve this answer



                                              share|improve this answer










                                              answered May 3 at 14:00









                                              Root G

                                              5221215




                                              5221215






















                                                  up vote
                                                  0
                                                  down vote













                                                  Lets take the example from JavaSE 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) {
                                                  ...
                                                  }
                                                  }


                                                  So why a list of dogs (circles) should not be considered implicitly a list of animals (shapes) is because of this situation:



                                                  // drawAll method call
                                                  drawAll(circleList);


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


                                                  So Java "architects" had 2 options which address this problem:




                                                  1. do not consider that a subtype is implicitly it's supertype, and give a compile error, like it happens now


                                                  2. consider the subtype to be it's supertype and restrict at compile the "add" method (so in the drawAll method, if a list of circles, subtype of shape, would be passed, the compiler should detected that and restrict you with compile error into doing that).



                                                  For obvious reasons, that chose the first way.






                                                  share|improve this answer

























                                                    up vote
                                                    0
                                                    down vote













                                                    Lets take the example from JavaSE 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) {
                                                    ...
                                                    }
                                                    }


                                                    So why a list of dogs (circles) should not be considered implicitly a list of animals (shapes) is because of this situation:



                                                    // drawAll method call
                                                    drawAll(circleList);


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


                                                    So Java "architects" had 2 options which address this problem:




                                                    1. do not consider that a subtype is implicitly it's supertype, and give a compile error, like it happens now


                                                    2. consider the subtype to be it's supertype and restrict at compile the "add" method (so in the drawAll method, if a list of circles, subtype of shape, would be passed, the compiler should detected that and restrict you with compile error into doing that).



                                                    For obvious reasons, that chose the first way.






                                                    share|improve this answer























                                                      up vote
                                                      0
                                                      down vote










                                                      up vote
                                                      0
                                                      down vote









                                                      Lets take the example from JavaSE 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) {
                                                      ...
                                                      }
                                                      }


                                                      So why a list of dogs (circles) should not be considered implicitly a list of animals (shapes) is because of this situation:



                                                      // drawAll method call
                                                      drawAll(circleList);


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


                                                      So Java "architects" had 2 options which address this problem:




                                                      1. do not consider that a subtype is implicitly it's supertype, and give a compile error, like it happens now


                                                      2. consider the subtype to be it's supertype and restrict at compile the "add" method (so in the drawAll method, if a list of circles, subtype of shape, would be passed, the compiler should detected that and restrict you with compile error into doing that).



                                                      For obvious reasons, that chose the first way.






                                                      share|improve this answer












                                                      Lets take the example from JavaSE 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) {
                                                      ...
                                                      }
                                                      }


                                                      So why a list of dogs (circles) should not be considered implicitly a list of animals (shapes) is because of this situation:



                                                      // drawAll method call
                                                      drawAll(circleList);


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


                                                      So Java "architects" had 2 options which address this problem:




                                                      1. do not consider that a subtype is implicitly it's supertype, and give a compile error, like it happens now


                                                      2. consider the subtype to be it's supertype and restrict at compile the "add" method (so in the drawAll method, if a list of circles, subtype of shape, would be passed, the compiler should detected that and restrict you with compile error into doing that).



                                                      For obvious reasons, that chose the first way.







                                                      share|improve this answer












                                                      share|improve this answer



                                                      share|improve this answer










                                                      answered Feb 27 '16 at 13:00









                                                      aurelius

                                                      1,98021949




                                                      1,98021949






















                                                          up vote
                                                          0
                                                          down vote













                                                          We should also take in consideration how the compiler threats the generic classes: in "instantiates" a different type whenever we fill the generic arguments.



                                                          Thus we have ListOfAnimal, ListOfDog, ListOfCat, etc, which are distinct classes that end up being "created" by the compiler when we specify the generic arguments. And this is a flat hierarchy (actually regarding to List is not a hierarchy at all).



                                                          Another argument why covariance doesn't make sense in case of generic classes is the fact that at base all classes are the same - are List instances. Specialising a List by filling the generic argument doesn't extend the class, it just makes it work for that particular generic argument.






                                                          share|improve this answer

























                                                            up vote
                                                            0
                                                            down vote













                                                            We should also take in consideration how the compiler threats the generic classes: in "instantiates" a different type whenever we fill the generic arguments.



                                                            Thus we have ListOfAnimal, ListOfDog, ListOfCat, etc, which are distinct classes that end up being "created" by the compiler when we specify the generic arguments. And this is a flat hierarchy (actually regarding to List is not a hierarchy at all).



                                                            Another argument why covariance doesn't make sense in case of generic classes is the fact that at base all classes are the same - are List instances. Specialising a List by filling the generic argument doesn't extend the class, it just makes it work for that particular generic argument.






                                                            share|improve this answer























                                                              up vote
                                                              0
                                                              down vote










                                                              up vote
                                                              0
                                                              down vote









                                                              We should also take in consideration how the compiler threats the generic classes: in "instantiates" a different type whenever we fill the generic arguments.



                                                              Thus we have ListOfAnimal, ListOfDog, ListOfCat, etc, which are distinct classes that end up being "created" by the compiler when we specify the generic arguments. And this is a flat hierarchy (actually regarding to List is not a hierarchy at all).



                                                              Another argument why covariance doesn't make sense in case of generic classes is the fact that at base all classes are the same - are List instances. Specialising a List by filling the generic argument doesn't extend the class, it just makes it work for that particular generic argument.






                                                              share|improve this answer












                                                              We should also take in consideration how the compiler threats the generic classes: in "instantiates" a different type whenever we fill the generic arguments.



                                                              Thus we have ListOfAnimal, ListOfDog, ListOfCat, etc, which are distinct classes that end up being "created" by the compiler when we specify the generic arguments. And this is a flat hierarchy (actually regarding to List is not a hierarchy at all).



                                                              Another argument why covariance doesn't make sense in case of generic classes is the fact that at base all classes are the same - are List instances. Specialising a List by filling the generic argument doesn't extend the class, it just makes it work for that particular generic argument.







                                                              share|improve this answer












                                                              share|improve this answer



                                                              share|improve this answer










                                                              answered Mar 2 at 7:48









                                                              Cristik

                                                              16.9k124175




                                                              16.9k124175






















                                                                  up vote
                                                                  0
                                                                  down vote













                                                                  The problem has been well-identified. But there's a solution; make doSomething generic:



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


                                                                  now you can call doSomething with either List<Dog> or List<Cat> or List<Animal>.






                                                                  share|improve this answer

























                                                                    up vote
                                                                    0
                                                                    down vote













                                                                    The problem has been well-identified. But there's a solution; make doSomething generic:



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


                                                                    now you can call doSomething with either List<Dog> or List<Cat> or List<Animal>.






                                                                    share|improve this answer























                                                                      up vote
                                                                      0
                                                                      down vote










                                                                      up vote
                                                                      0
                                                                      down vote









                                                                      The problem has been well-identified. But there's a solution; make doSomething generic:



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


                                                                      now you can call doSomething with either List<Dog> or List<Cat> or List<Animal>.






                                                                      share|improve this answer












                                                                      The problem has been well-identified. But there's a solution; make doSomething generic:



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


                                                                      now you can call doSomething with either List<Dog> or List<Cat> or List<Animal>.







                                                                      share|improve this answer












                                                                      share|improve this answer



                                                                      share|improve this answer










                                                                      answered Mar 28 at 18:56









                                                                      gerardw

                                                                      2,7482127




                                                                      2,7482127






















                                                                          up vote
                                                                          0
                                                                          down vote













                                                                          another solution is to build a new list



                                                                          List<Dog> dogs = new ArrayList<Dog>(); 
                                                                          List<Animal> animals = new ArrayList<Animal>(dogs);
                                                                          animals.add(new Cat());





                                                                          share|improve this answer

























                                                                            up vote
                                                                            0
                                                                            down vote













                                                                            another solution is to build a new list



                                                                            List<Dog> dogs = new ArrayList<Dog>(); 
                                                                            List<Animal> animals = new ArrayList<Animal>(dogs);
                                                                            animals.add(new Cat());





                                                                            share|improve this answer























                                                                              up vote
                                                                              0
                                                                              down vote










                                                                              up vote
                                                                              0
                                                                              down vote









                                                                              another solution is to build a new list



                                                                              List<Dog> dogs = new ArrayList<Dog>(); 
                                                                              List<Animal> animals = new ArrayList<Animal>(dogs);
                                                                              animals.add(new Cat());





                                                                              share|improve this answer












                                                                              another solution is to build a new list



                                                                              List<Dog> dogs = new ArrayList<Dog>(); 
                                                                              List<Animal> animals = new ArrayList<Animal>(dogs);
                                                                              animals.add(new Cat());






                                                                              share|improve this answer












                                                                              share|improve this answer



                                                                              share|improve this answer










                                                                              answered Jul 20 at 14:12









                                                                              ejaenv

                                                                              9681121




                                                                              9681121

















                                                                                  protected by Aniket Thakur Oct 2 '15 at 18:51



                                                                                  Thank you for your interest in this question.
                                                                                  Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).



                                                                                  Would you like to answer one of these unanswered questions instead?



                                                                                  Popular posts from this blog

                                                                                  Full-time equivalent

                                                                                  さくらももこ

                                                                                  13 indicted, 8 arrested in Calif. drug cartel investigation