This week's book giveaway is in the OCAJP 8 forum. We're giving away four copies of OCA Java SE 8 Programmer I Study Guide and have Edward Finegan & Robert Liguori on-line! See this thread for details.
With reference to the link I pointed to you, yesterday...
The generic type <? extends B> of the reference means, the generic type of the object can be either B, or anything which extends B. For example, if 'C' is a sub class of 'B', we can use...
While the compiler doesn't know that exact runtime type (B or C or whatever that extends B...), it doesn't allow you to pass anything to the add() method here - because the object you passed to the add method should be in the bound of the runtime type. Please read that article again, and think the wise-versa of using 'super'.
The answer is simple and two fold. Generics are only enforced during compile time by the java compiler and can't be enforced during runtime by the JVM. Repeat with me Generics only provide compile time safety, Generics only provide compile time safety . So why doesn't the JVM provide run-time checks on Generic code? The reason is in order to make the Generics code compatible with legacy non-Generic code prior to Java 1.5 so that both could potentially inter-operate and not to break all legacy code. The 2nd reason why the add() above isn't allowed is because wildcards in generic types, namely <? extends B> provides polymorphism in generics and that comes with a price.
During compile time, the java compiler will check to make sure that the generic type you specify within the angled brackets <....> is compatible to what is assigned to it. So in the above case, it can check that <B> is indeed compatible with <? extends B>. So the compiler is happy with it. But ls.add(new C()), even though is a valid one (i.e. ArrayList<B> can contain an object of type C() in it), the problem with that operation is that the add operation is a run-time operation done by JVM and the JVM during run-time cannot check to make sure that the object you are trying to add to ArrayList<B> is indeed of type B. The natural question is why not? OK, this is the most important concept you need to burn into your brain: The reason JVM can't enforce a run-time check on the object being added to the ArrayList<B> ls, is because the JVM at run-time doesn't know that ls is only supposed to contain objects of type <B>. Why not? It is because of Type Erasure. What is Type Erasure? Once the compiler compiles the code and enforces all the compile time checks, it simply removes the Generic type information within the angled brackets and makes the byte code as if there were no Generics at all. Why? This is to make things compatible with the legacy non-generic code and not to break the non-generic code that has been written. So due to type erasure, the jvm doesn't know anything about the type being added and it can't throw a run-time exception if you try to add an in-compatible type. Since this can't be done, this operation of trying to add() to a Generic type code that is of type say ArrayList<? extends B> is considered UNSAFE and it won't be allowed by the compiler.
But if you had declared your List as
List<B> ls = new ArrayList<B>();, then obviously you can do
becuase this time the compiler knows for sure that only B types could be allowed to be added. So it can make sure that anything added using the add() method is compatible or it can throw an error. So add() is considered safe. But in the previous case, the compiler does know that anything of type B could be allowed to be added, but the problem is with <? extends B>, which means any type of B could be added to ls. The compiler has no way of knowing what types of B exist out there or could show up in the future and it considers add() potentially unsafe in this polymorphic context and WON'T allow this UNSAFE operation.
But if you use <? super B> then it will allow ls.add(new B()), because the compiler does know what are the super types of B and it can safely allow add() operations since it can enforce compile time checks, but it can't possibly know what could be the potential sub-types of B and can't allow add() when using <? extends B>.
This simply works because of the polymorphic nature of wildcard types in generics. Above you are using polymorphic reference types. L2 is any sub-type of Object <? extends Object> is the same as <?>, which means any other generic type will fit into it....I mean anything. So you can assign any other generic reference to List<? extends Object>. But the reverse is not true. In your code try doing L1 = L2 and it won't work, since you can go the other way around i.e. <? super Number> is not of type <? extends Object>, but <? extends Object> is of type <? super Number>. Is that clear?
Hope that helps. If anyone has corrections or have something more to add, feel free.