In Chapter 8 of Generic Types from Core Java Volume I Edition 10,
NOTE: Another common use for supertype bounds is an argument type of a functional interface. For example, the Collection interface has a method
default boolean removeIf(Predicate<? super E> filter)
The method removes all elements that fulfill the given predicate. For example, if you hate employees with odd hash codes, you can remove them like this:
ArrayList<Employee> staff = . . .; Predicate<Object> oddHashCode = obj -> obj.hashCode() %2 != 0; staff.removeIf(oddHashCode);
You want to be able to pass a
Predicate<Object>
, not just aPredicate<Employee>
. The super wildcard makes that possible.
I met some problems when trying to understand this, so <? super E>
means that filter could point to any predicate type that can be superclass of Employee
or Employee
itself.
The text above mentioned we could pass a Predicate<Object>
to Predicate<? super E>
.
But what if Predicate<? super E>
points to Predicate<Employee>
, can Predicate<Object>
be passed to Predicate<Employee>
?
Did I misunderstand something?
Your understanding is correct. For example, a function that takes Predicate<? super Employee>
(as in your ArrayList<Employee>
example) can also accept a Predicate<Object>
like Objects::nonNull
. Conceptually, this makes sense for the following reason: "We (the class) take a Predicate
that operates on us, or on any of the (transitive) superclasses of ourself, because we have an is-a relationship with those superclasses." That is to say, a function that takes any Object
and returns a boolean
is equivalently applicable to Employee
because Employee
is-a Object
, and so it is valid to apply this function to Employee
. The derived class is not the exact same as the base class, but the predicate (logical test) still applies to the derived class, because it makes sense to talk about the derived class as-a base class.
Let's go through an example: Employees
could be derived from Person
. If
Person
can be tested with a Predicate<Person>
called hasJob
, then it is logically sound to be able to test Employees
for hasJob
as well. The ability of that function to take Predicate<? super Employee>
instead of just Predicate<Employee>
is required to maintain the ability of the function to take a logically sound predicate. On the other hand, if you only expect for Employee
s to be tested for some property, you might accept only a Predicate<Employee>
instead, because that corresponds to the logical soundness of only Employee
and its derived classes possessing the ability to be tested for that property.
To be 100% clear about what is going on here:
Predicate<? super Employee>
accepts Predicate
that test
s Employee
and any superclass of Employee
, including Object
Predicate<Employee>
accepts Predicate
that test
s Employee
and any subclass of Employee
, which excludes Object
Given this class hierarchy: SalariedEmployee is-a Employee is-a Person
, here's what happens (P is shorthand for Predicate
):
╔══════════════════╦═══════════╦═══════════════════╦═════════════╦═════════════════════╦═════════════════════╦═════════════════════════════╗
║ Type ║ P<Person> ║ P<? super Person> ║ P<Employee> ║ P<? super Employee> ║ P<SalariedEmployee> ║ P<? super SalariedEmployee> ║
╠══════════════════╬═══════════╬═══════════════════╬═════════════╬═════════════════════╬═════════════════════╬═════════════════════════════╣
║ Person ║ Accept ║ Accept ║ Reject ║ Accept ║ Reject ║ Accept ║
║ Employee ║ Accept ║ Reject ║ Accept ║ Accept ║ Reject ║ Accept ║
║ SalariedEmployee ║ Accept ║ Reject ║ Accept ║ Reject ║ Accept ║ Accept ║
║ Object ║ Reject ║ Accept ║ Reject ║ Accept ║ Reject ║ Accept ║
╚══════════════════╩═══════════╩═══════════════════╩═════════════╩═════════════════════╩═════════════════════╩═════════════════════════════╝
Note that Accept/Reject
denote the types that can be fed into the Predicate
, not the actual result of the Predicate
.