Types of argument to be passed to <? super T> in Java

Holmes Queen :

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 a Predicate<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?

Avi :

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 Employees 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 tests Employee and any superclass of Employee, including Object
  • Predicate<Employee> accepts Predicate that tests 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.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=308130&siteId=1