From page 49 of OCP Java SE 6 Programmer Practice Exams, question 11. We are given such code:
class A { }
class B extends A { }
class C extends B { }
public class Carpet<V extends B> {
public <X extends V> Carpet<? extends V> method(Carpet<? super X> e) {
// insert code here
}
}
And we are asked: Which, inserted independently at marked line, will compile?
So, I'm testing the first answer - return new Carpet<X>()
. It works. Ok, my reasoning goes like well, if I have a return type of Carpet<? extends V>
, it must return something that extends V
. Just before it's written, that X extends V
, so that's perfect, X
works.
Next answer - return new Carpet<V>()
. Well, this is even simpler, because V
already "extends" V
, so I can return that. It works, ok, good.
Next - return new Carpet<A>()
. This is not working, compiler is shouting:
Type parameter 'A' is not within its bound; should extend 'B'
But, I agree. It's written near the class declaration, V extends B
. So, I understand I can return something that extends V
, which extends B
. For sure not A
.
So, I see the next answer - return new Carpet<B>()
. Phew, easy, the compiler just said "should extend B
, so B
(like before V
for "something that extends V
") should be just perfect. But! It's not compiling:
Error:(6, 16) java: incompatible types
required: Carpet<? extends V>
found: Carpet<B>
I don't understand why. Explanation given in book is:
It's illegal to use a concrete class type since the exact scope of V is unknown.
But frankly, I still don't get it. I'm not sure what the "exact scope" in that context means, but the compiler said, that return type parameter should extend B
. So, he is able to figure it out (like me) that "something that extends V
" is "something that extends B
". So, why B
is not working?
Could you please provide better explanation?
V is the generic type of Carpet. When instantiating a Carpet, the user of the class chooses the concrete type of V. It could be B, or C, or any other class that extends B directly or indirectly:
Carpet<B> carpetOfB = new Carpet<B>();
or
Carpet<C> carpetOfC = new Carpet<C>();
or
Carpet<Plane> carpetOfPlane = new Carpet<Plane>();
Whatever the user chooses as concrete type for V (let's say he chooses Plane, which extends C), the method must return a Carpet of a type that extends V. So, in this case, this type must be Plane or a subtype of Plane. And clearly, B doesn't qualify, since it doesn't extend Plane.