For the comparing source code in Comparator class
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable) (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
I understand the difference between super
and extends
. What i dont understand is that why this method have them. Can someone give me an example on what cannot be achieved when the parameter look like this Function<T, U> keyExtractor
?
For example :
Comparator<Employee> employeeNameComparator = Comparator.comparing(Employee::getName);
can also compile with the following function definition
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<T, U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable) (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
Comparable vs Comparator. Comparable interface can be used to provide single way of sorting whereas Comparator interface is used to provide different ways of sorting. For using Comparable, Class needs to implement it whereas for using Comparator we don't need to make any change in the class.
Comparator detaches the sorting logic from your object and contains the compareTo() logic within your sort() method.
The compare Method obj1 and obj2 are the objects to be compared. This method returns zero if the objects are equal. It returns a positive value if obj1 is greater than obj2. Otherwise, a negative value is returned.
Comparator has undergone a major overhaul in Java 8 while still retaining its essence which is to compare and sort objects in Collections. Comparator now supports declarations via lambda expressionsRead Lambda Expressions Tutorial as it is a Functional Interface.
Here is a simple example: comparing cars by weight. I will first describe the problem in text-form, and then demonstrate every possible way how it can go wrong if either ? extends
or ? super
is omitted. I also show the ugly partial workarounds that are available in every case. If you prefer code over prose, skip directly to the second part, it should be self-explanatory.
First, the contravariant ? super T
.
Suppose that you have two classes Car
and PhysicalObject
such that Car extends PhysicalObject
. Now suppose that you have a function Weight
that extends Function<PhysicalObject, Double>
.
If the declaration were Function<T,U>
, then you couldn't reuse the function Weight extends Function<PhysicalObject, Double>
to compare two cars, because Function<PhysicalObject, Double>
would not conform to Function<Car, Double>
. But you obviously want to be able to compare cars by their weight. Therefore, the contravariant ? super T
makes sense, so that Function<PhysicalObject, Double>
conforms to Function<? super Car, Double>
.
Now the covariant ? extends U
declaration.
Suppose that you have two classes Real
and PositiveReal
such that PositiveReal extends Real
, and furthermore assume that Real
is Comparable
.
Suppose that your function Weight
from the previous example actually has a slightly more precise type Weight extends Function<PhysicalObject, PositiveReal>
. If the declaration of keyExtractor
were Function<? super T, U>
instead of Function<? super T, ? extends U>
, you wouldn't be able to make use of the fact that PositiveReal
is also a Real
, and therefore two PositiveReal
s couldn't be compared with each other, even though they implement Comparable<Real>
, without the unnecessary restriction Comparable<PositiveReal>
.
To summarize: with the declaration Function<? super T, ? extends U>
, the Weight extends Function<PhysicalObject, PositiveReal>
can be substituted for a Function<? super Car, ? extends Real>
to compare Car
s using the Comparable<Real>
.
I hope this simple example clarifies why such a declaration is useful.
? extends
or ? super
is omittedHere is a compilable example with a systematic enumeration of all things that can possibly go wrong if we omit either ? super
or ? extends
. Also, two (ugly) partial work-arounds are shown.
import java.util.function.Function;
import java.util.Comparator;
class HypotheticComparators {
public static <A, B> Comparator<A> badCompare1(Function<A, B> f, Comparator<B> cb) {
return (A a1, A a2) -> cb.compare(f.apply(a1), f.apply(a2));
}
public static <A, B> Comparator<A> badCompare2(Function<? super A, B> f, Comparator<B> cb) {
return (A a1, A a2) -> cb.compare(f.apply(a1), f.apply(a2));
}
public static <A, B> Comparator<A> badCompare3(Function<A, ? extends B> f, Comparator<B> cb) {
return (A a1, A a2) -> cb.compare(f.apply(a1), f.apply(a2));
}
public static <A, B> Comparator<A> goodCompare(Function<? super A, ? extends B> f, Comparator<B> cb) {
return (A a1, A a2) -> cb.compare(f.apply(a1), f.apply(a2));
}
public static void main(String[] args) {
class PhysicalObject { double weight; }
class Car extends PhysicalObject {}
class Real {
private final double value;
Real(double r) {
this.value = r;
}
double getValue() {
return value;
}
}
class PositiveReal extends Real {
PositiveReal(double r) {
super(r);
assert(r > 0.0);
}
}
Comparator<Real> realComparator = (Real r1, Real r2) -> {
double v1 = r1.getValue();
double v2 = r2.getValue();
return v1 < v2 ? 1 : v1 > v2 ? -1 : 0;
};
Function<PhysicalObject, PositiveReal> weight = p -> new PositiveReal(p.weight);
// bad "weight"-function that cannot guarantee that the outputs
// are positive
Function<PhysicalObject, Real> surrealWeight = p -> new Real(p.weight);
// bad weight function that works only on cars
// Note: the implementation contains nothing car-specific,
// it would be the same for every other physical object!
// That means: code duplication!
Function<Car, PositiveReal> carWeight = p -> new PositiveReal(p.weight);
// Example 1
// badCompare1(weight, realComparator); // doesn't compile
//
// type error:
// required: Function<A,B>,Comparator<B>
// found: Function<PhysicalObject,PositiveReal>,Comparator<Real>
// Example 2.1
// Comparator<Car> c2 = badCompare2(weight, realComparator); // doesn't compile
//
// type error:
// required: Function<? super A,B>,Comparator<B>
// found: Function<PhysicalObject,PositiveReal>,Comparator<Real>
// Example 2.2
// This compiles, but for this to work, we had to loosen the output
// type of `weight` to a non-necessarily-positive real number
Comparator<Car> c2_2 = badCompare2(surrealWeight, realComparator);
// Example 3.1
// This doesn't compile, because `Car` is not *exactly* a `PhysicalObject`:
// Comparator<Car> c3_1 = badCompare3(weight, realComparator);
//
// incompatible types: inferred type does not conform to equality constraint(s)
// inferred: Car
// equality constraints(s): Car,PhysicalObject
// Example 3.2
// This works, but with a bad code-duplicated `carWeight` instead of `weight`
Comparator<Car> c3_2 = badCompare3(carWeight, realComparator);
// Example 4
// That's how it's supposed to work: compare cars by their weights. Done!
Comparator<Car> goodComparator = goodCompare(weight, realComparator);
}
}
Related links
Let's say, for example, we want to compare commercial flights by what plane they use. We would therefore need a method that takes in a flight, and returns a plane:
Plane func (CommercialFlight)
That is, of course, a Function<CommercialFlight, Plane>
.
Now, the important thing is that the function returns a Plane
. It doesn't matter what kind of plane is returned. So a method like this should also work:
CivilianPlane func (CommercialFlight)
Now technically this is a Function<CommercialFlight, CivilianPlane>
, which is not the same as a Function<CommercialFlight, Plane>. So without the
extends`, this function wouldn't be allowed.
Similarly, the other important thing is that is can accept a CommercialFlight
as an argument. So a method like this should also work:
Plane func (Flight)
Technically, this is a Function<Flight, Plane>
, which is also not the same as a Function<CommercialFlight, Plane>
. So without the super
, this function wouldn't be allowed either.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With