En el capítulo 8 de los tipos genéricos de volumen del núcleo de Java I Edición 10,
NOTA: Otro uso común de los límites del supertipo es un tipo de argumento de una interfaz funcional. Por ejemplo, la interfaz Collection tiene un método
default boolean removeIf(Predicate<? super E> filter)
El método elimina todos los elementos que cumplen el predicado dado. Por ejemplo, si no le gusta a los empleados con códigos hash impares, se pueden quitar así:
ArrayList<Employee> staff = . . .; Predicate<Object> oddHashCode = obj -> obj.hashCode() %2 != 0; staff.removeIf(oddHashCode);
¿Quieres ser capaz de pasar una
Predicate<Object>
, no sólo unaPredicate<Employee>
. El súper comodín hace posible.
Me encontré con algunos problemas cuando se trata de entender esto, por lo que <? super E>
los medios de filtro que podrían apuntar a cualquier tipo de predicados que pueden ser superclase de Employee
o Employee
sí.
El texto anterior se menciona que podría pasar una Predicate<Object>
a Predicate<? super E>
.
Pero lo que si Predicate<? super E>
los puntos a Predicate<Employee>
, puede Predicate<Object>
ser pasado a Predicate<Employee>
?
¿He entendido mal algo?
Su comprensión es correcta. Por ejemplo, una función que toma Predicate<? super Employee>
(como en el ArrayList<Employee>
ejemplo) también puede aceptar un Predicate<Object>
igual Objects::nonNull
. Conceptualmente, esto tiene sentido por la siguiente razón: "Nosotros (la clase) tomamos una Predicate
que opera en nosotros, o en cualquiera de los (transitivo) superclases de nosotros mismos, porque tenemos un es-una relación con esos superclases." Es decir, una función que toma cualquier Object
y devuelve una boolean
es equivalente, aplicable a Employee
causa Employee
is-a Object
, y por lo que es válido aplicar esta función aEmployee
. La clase derivada no es exactamente el mismo que la clase base, pero el predicado (prueba lógica) todavía se aplica a la clase derivada, porque tiene sentido hablar de la clase derivada as-a clase base.
Vamos a ir a través de un ejemplo: Employees
se podría derivar de Person
. Si Person
se puede probar con una Predicate<Person>
llama hasJob
, entonces es lógicamente suena para ser capaz de prueba Employees
para hasJob
así. La capacidad de esa función para tomar Predicate<? super Employee>
en lugar de sólo Predicate<Employee>
es necesaria para mantener la capacidad de la función para tomar un predicado lógicamente sonido. Por otro lado, si sólo se espera de Employee
s para hacerse la prueba de alguna propiedad, es posible aceptar solamente una Predicate<Employee>
vez, ya que corresponde a la solidez lógica de solo Employee
y sus clases derivadas que poseen la capacidad de hacerse la prueba de esa propiedad.
Para ser 100% claro sobre lo que está pasando aquí:
Predicate<? super Employee>
aceptaPredicate
quetest
sEmployee
y cualquier superclase deEmployee
, incluyendoObject
Predicate<Employee>
aceptaPredicate
quetest
sEmployee
y cualquier subclase deEmployee
, que excluyeObject
Teniendo en cuenta esta jerarquía de clases: SalariedEmployee is-a Employee is-a Person
, esto es lo que sucede (P es la abreviatura de 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 ║
╚══════════════════╩═══════════╩═══════════════════╩═════════════╩═════════════════════╩═════════════════════╩═════════════════════════════╝
Nota que Accept/Reject
denotan los tipos que se pueden alimentar en el Predicate
, no el resultado real de la Predicate
.