public class SS {
public static void main(String[] args) {
SS s = new SS();
List<B> list = new ArrayList<>();
s.checkWithoutInheritance(Arrays.asList(new B())); //Works fine
s.checkWithoutInheritance(list); //----------Not working
s.checkWithInheritance(Arrays.asList(new B())); //Works fine
s.checkWithInheritance(list); //Works fine
}
private void checkWithInheritance(final Collection<? extends A> s) {
}
private void checkWithoutInheritance(final Collection<A> s) {
}
}
class A {
};
class B extends A {
};

- 2,711
- 2
- 13
- 23
3 Answers
They don't behave differently, you are using them differently.
Here you are letting the compiler decide the type of the List
returned by Arrays.asList
, and the compiler decides it should be List<A>
, given the argument required by checkWithoutInheritance()
:
s.checkWithoutInheritance(Arrays.asList(new B()));
Here you are passing a List<B>
to the method, so the compiler has no choice but to fail:
List<B> list = new ArrayList<>();
s.checkWithoutInheritance(list);
If you used both List
s the same you'd get the same output:
Both failing:
List<B> list = new ArrayList<>();
List<B> list2 = Arrays.asList(new B());
s.checkWithoutInheritance(list2);
s.checkWithoutInheritance(list);
Both working:
s.checkWithoutInheritance(Arrays.asList(new B()));
s.checkWithoutInheritance(new ArrayList<>());
As for checkWithInheritance(final Collection<? extends A> s)
, it accepts both a List<A>
and a List<B>
, which is why both of your checkWithInheritance
calls pass compilation.

- 387,369
- 54
- 702
- 768
When you have a method:
void checkWithoutInheritance(final Collection<A> s);
The line:
s.checkWithoutInheritance(Arrays.asList(new B()));
works fine, because it's the same as:
List<A> list = Arrays.asList(new B());
s.checkWithoutInheritance(list);
But, the following code:
List<B> list = new ArrayList<B>();
s.checkWithoutInheritance(list); // compiler error
produces a compiler error, because generics are not covariant, and List<B>
can not be assigned to the List<A>
:
List<B> listb = new ArrayList<B>();
List<A> lista = listb; // Incompatible types error, even though B extends A
When you have a method:
void checkWithInheritance(final Collection<? extends A> s);
<? extends A>
wildcard relax the restrictions on the variable s
, therefore both are valid:
s.checkWithInheritance(new ArrayList<A>());
s.checkWithInheritance(new ArrayList<B>());
<? extends A>
works on lists of A
and the subtypes of A
.

- 14,685
- 6
- 61
- 90
List<B> list;
This declares a variable which is a list, with the guarantee that everything in that list is something that can be cast B
(i.e. B
, a subclass of B
, or null), to which you can only add things which can be cast to
B`.
private void checkWithoutInheritance(final Collection<A> s) {
This declares a method, accepting a parameter which is a collection, with the guarantee that everything in that collection can be cast to A
, to which you can only add things which can be cast to A
.
This means that checkWithoutInheritance
can do this:
s.add(new A());
If you were able to call
checkWithoutInheritance(list);
then list
would now contain an instance of A
, which cannot be cast to a B
, violating the guarantee on the contents of list
.
As such, it is disallowed by the compiler. The compiler doesn't care that you don't add an A
to s
, because you could. It errs on the side of safety.
On the other hand:
private void checkWithInheritance(final Collection<? extends A> s) {
This declares a method, accepting a parameter which is a collection, with the guarantee that everything in that collection is something which can be cast to some subclass of A
.
You can't add an A
to it, because you don't know if "some subclass" means A
, B
, or something else entirely.
As such, you can't write s.add(new A());
in its method body, so it is safe to pass a List<B>
to it.

- 137,514
- 11
- 162
- 243