<? super Animal> means animal or it's superclass only. Then why inside the method it's allowing to add Dog object even though it's a subclass of Animal? This program compiles and runs!

   import java.util.*;
    class Cat {}
    class Animal{Animal() {System.out.println("Animal constructor");}}
    class Dog extends Animal{Dog() {System.out.println("Dog constructor");}}

    public class GenericDemo5 extends Animal{
        public static void main(String r[]) {
            List l1 = new ArrayList(); //can add anything since no type here
            l1.add(new Dog());
            l1.add(new Animal());
            l1.add(30);
            met(l1);
            System.out.println(l1); }

        public static void met(List<? super Animal> l2) {
            l2.add(new Animal());
            l2.add(new Dog());
            System.out.println(l2); }}

Even with Generic Instantiation(shown below) same output!! This also compiles and runs!

import java.util.*;
class Cat {}
class Animal{Animal() {System.out.println("Animal constructor");}}
class Dog extends Animal{Dog() {System.out.println("Dog constructor");}}

public class GenericDemo11 extends Animal{
    public static void main(String r[]) {
        List<Animal> l1 = new ArrayList<Animal>(); //can add anything
        l1.add(new Dog());
        l1.add(new Animal());
        met(l1);
        System.out.println(l1); }

    public static void met(List<? super Animal> l2) {
        l2.add(new Animal());
        l2.add(new Dog());
        System.out.println(l2); }}

And it gets more peculiar! If I change it to Dog during instantiation and inside Wild Card, it says you can't add Animal object from within method, which according to me it should. <? super Dog> means Dog and it's superclass.

import java.util.*;
class Cat {}
class Animal{Animal() {System.out.println("Animal constructor");}}
class Dog extends Animal{Dog() {System.out.println("Dog constructor");}}

public class GenericDemo11 extends Animal{
    public static void main(String r[]) {
        List<Dog> l1 = new ArrayList<Dog>(); //can add anything
        l1.add(new Dog());
        met(l1);
        System.out.println(l1); }

    public static void met(List<? super Dog> l2) {
        l2.add(new Animal()); //ERROR!
        l2.add(new Dog());
        System.out.println(l2); }}

Help please! Thank you.

In the first two samples, you can add anything because the type that your list contains is a super class List<Animal>. All your classes that extends your Animal class can be added to your list since they are all considered as Animal. On the otherhand(3rd example) you can't add an Animal to a list that contains Dog becaus Dog is just a subclass of Animal. More like "not all Animals are Dogs, but all Dogs are Animal". Inheritance it is! :P

When you have something like void met(List<? super Animal> l2) you are saying that the parameter will be a List of type "x"where "x" is Animal or a superclass of Animal. Note that at compile time you don't know what "x" will be exactly - it could be Animal, it could be Object etc. That depends on who calls the method and what they pass, which in general cannot be known until run time.
Ex 1 - it doesn't matter what superclass of Animal "x" will be, a Dog wil always be a subclass of "x".
Ex2 - exactly the same.
Ex 3 - "x" could be Dog or any superclass of Dog. So its OK to pass a List<Dog> for that parameter. In which case adding an Animal would be invalid, so the code will not compile.

The learning point here is that the exact type of the value that's passed to met is irrelevant at compile time, as long as it fits the generic spec in met's definition. The compiler can only compile met based on its method signature.

Note that at compile time you don't know what "x" will be exactly - it could be Animal, it could be Object etc. That depends on who calls the method and what they pass, which in general cannot be known until run time.

Referring to James' line above, if I instantiate a non generic code and use it to call a method having <? super Animal>, we can add anything right? Since there is no type safe parameter during instantiation, why can't we add anything like a String or int from within the method called?

class Animal{Animal() {System.out.println("Animal constructor");}}
class Dog extends Animal{Dog() {System.out.println("Dog constructor");}}

public class GenericDemo5 extends Animal{
    public static void main(String r[]) {
        List l1 = new ArrayList(); //can add anything
        l1.add(new Dog());
        l1.add(new Animal());
        l1.add(30);
        met(l1);
        System.out.println(l1); }

    public static void met(List<? super Animal> l2) {
        l2.add(new Animal());
        l2.add(new Dog());
        l2.add(71);
        l2.add("hello");
        System.out.println(l2); }}

Line 17 and 18 can't be added, compiler error! When we are able to add an integer 30 by using li1.add(30) why this problem?

Here lies my exact confusion, if you can't add an int (71) and string("hello") because they are not Animal or superclass of Animal, how can Dog be added successfully?

Edited 4 Years Ago by rahul.ch

  1. When compiling main the compiler knows that l1 is a non-genericised list, so you can add anything to it.
  2. When compiling the met method the compiler knows that any parameter passed will be a List of Animal or a superclass of Animal. Maybe when met is called the parameter will actually be a List<Object>, or even just a List, but maybe it will be a List<Animal>. In this case you happen to know that the value passed as parameter will be a List, but tomorrow I may write a method that calls met and passes a List<Animal>. "hello" and 12 are not Animals, so the compiler cannot guarantee that the add is OK.
    You can add a Dog because a Dog is an Animal - all Dogs are Animals.
import java.util.*;
class Animal{Animal() {System.out.println("Animal constructor");}}
class Dog extends Animal{Dog() {System.out.println("Dog constructor");}}

public class GenericDemo7 {
    public static void main(String r[]) {
        List<Animal> l1 = new ArrayList<Animal>(); 
        l1.add(new Dog());
        l1.add(new Animal());
        met(l1);
        System.out.println(l1); }

    public static void met(List l2) {
        l2.add(new Integer(3)); 
        l2.add(3.14); 
        l2.add(new Animal());
        System.out.println(l2); }}

How do I interpret this now? The var l1 is instatantiated to be type safe so after l1 is passed to a non generic method, does the type get erased and allows addition of Integer and double?

All met knows is that you will pass it a List as a parameter, so there's no compile-time checking it can do on what you add.
Remember the method is compiled based on how you declare it. The compiler cannot know everything about the actual objects that you (or I) may pass it at runtime.

Understandable that at compile time doesn't know what will be passed to met() since it's parameter is a non gen List but at Runtime why isn't ClassCastException thrown for incompatible elements stored in List l1 which allows ONLY Animal or Dog?

It's the way generics were implemented as a compile-time thing. In the .class files there are no generic types - it's called "type erasure". Your generic info has been "erased" by the time the code is executed, so there's no info that would cause a class cast ex. At run time your List<? extends Dog> is just a List.
Some people (me included) think it's a great shame they used type erasure. As I understand it they took the aproach they did because there were technical cases where a better implementation would fail. Personally I think i would have been better to let those fail (runtime exception maybe) and give us proper run-time generics for the 99% of cases where it would be good. But then I don't work for Sun/Oracle, so what I think is irrelevant.
More to the point this explains what and why.

Hmmm thanks! Read up on type erasure after you explained :) After understanding the concept even I agree with your opinion.

This question has already been answered. Start a new discussion instead.