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
.