I just tested it, the assignment itself compiles. What changes is whether you can actually call predicate.test()
.
Let's take a step back and use a placeholder GenericClass<T>
for explanation. For type arguments, Foo
extends Bar
and Bar
extends Baz
.
Extends:
When you declare a GenericClass<? extends Bar>
, you are saying "I don't know what its generic type argument actually is, but it's a subclass of Bar
." The actual instance will always have a non-wildcard type argument, but in this part of the code you don't know what the value of it is. Now consider what that means for method invocations.
You know what you've actually got is either a GenericClass<Foo>
or a GenericClass<Bar>
. Consider a method that returns T
. In the former case, its return type is Foo
. In the latter, Bar
. Either way, it's a subtype of Bar
and is safe to assign to a Bar
variable.
Consider a method that has a T
parameter. If it's a GenericClass<Foo>
, then passing it a Bar
is an error - Bar
is not a subtype of Foo
.
So, with an upper bound you can use generic return values, but not generic method parameters.
Super:
When you declare a GenericClass<? super Bar>
, you are saying "I don't know what its generic type argument actually is, but it's a superclass of Bar
." Now consider what that means for method invocations.
You know what you've actually got is either a GenericClass<Bar>
or a GenericClass<Baz>
. Consider a method that returns T
. In the former case, it returns Bar
. In the latter, Baz
. If it returns a Baz
, then assigning that value to a Bar
variable is an error. You don't know which it is, so you can't safely assume anything here.
Consider a method that has a T
parameter. If it's a GenericClass<Bar>
, then passing it a Bar
is legal. If it's a GenericClass<Baz>
, then passing it a Bar
is still legal because Bar
is a subtype of Baz
.
So, with a lower bound you can use generic method parameters, but not generic return values.
In summary: <? extends T>
means you can use generic return values but not parameters. <? super T>
means you can use generic parameters but not return values. Predicate.test()
has a generic parameter, so you need super
.
Another thing to consider: The bounds stated by a wildcard are about the actual type argument of the object. Their consequences on the types that you can use with that object are the opposite. An upper bound wildcard (extends
) is a lower bound on the types of variables you can assign return values to. A lower bound wildcard (super
) is an upper bound on the types you can pass in as parameters. predicate.test(new Object())
will not compile because, with a lower bound of String
, it will only accept subclasses of String
.
More explanations: Guidelines for Wildcard Use.