Java 8 Comparator比较静态函数
Java 8 Comparator comparing static function
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));
}
我明白 super
和 extends
之间的区别。我不明白的是为什么这种方法有它们。有人可以给我一个例子,当参数看起来像这样 Function<T, U> keyExtractor
时无法实现什么?
例如:
Comparator<Employee> employeeNameComparator = Comparator.comparing(Employee::getName);
也可以用下面的函数定义编译
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));
}
比方说,我们想比较商业航班使用的飞机。因此,我们需要一种方法来进行飞行,并且 returns 一架飞机:
Plane func (CommercialFlight)
当然也就是一个Function<CommercialFlight, Plane>
.
现在,重要的是函数returns一个Plane
。返回什么样的飞机并不重要。所以像这样的方法也应该有效:
CivilianPlane func (CommercialFlight)
从技术上讲,这是一个 Function<CommercialFlight, CivilianPlane>
,它与 Function<CommercialFlight, Plane>. So without the
extends 不同,不允许使用此功能。
同样,另一个重要的事情是可以接受 CommercialFlight
作为参数。所以像这样的方法也应该有效:
Plane func (Flight)
从技术上讲,这是 Function<Flight, Plane>
,也不同于 Function<CommercialFlight, Plane>
。所以如果没有 super
,这个函数也不会被允许。
这是一个简单的例子:按重量比较汽车。我将首先描述 text-form 中的问题,然后演示如果省略 ? extends
或 ? super
中的每一个可能出错的方式。我还展示了在每种情况下都可用的丑陋的部分解决方法。 如果你更喜欢代码而不是散文,直接跳到第二部分,应该是self-explanatory。
问题的非正式讨论
首先,逆变? super T
.
假设您有两个 类 Car
和 PhysicalObject
使得 Car extends PhysicalObject
。现在假设您有一个扩展 Function<PhysicalObject, Double>
.
的函数 Weight
如果声明是 Function<T,U>
,那么你不能重用函数 Weight extends Function<PhysicalObject, Double>
来比较两辆车,因为 Function<PhysicalObject, Double>
不符合 Function<Car, Double>
。但是您显然 想要 能够按重量比较汽车。因此,逆变? super T
是有道理的,所以Function<PhysicalObject, Double>
符合Function<? super Car, Double>
.
现在是协变 ? extends U
声明。
假设你有两个类Real
和PositiveReal
使得PositiveReal extends Real
,并且进一步假设Real
是Comparable
。
假设上一个示例中的函数 Weight
实际上具有稍微更精确的类型 Weight extends Function<PhysicalObject, PositiveReal>
。如果 keyExtractor
的声明是 Function<? super T, U>
而不是 Function<? super T, ? extends U>
,你将无法利用 PositiveReal
也是 Real
的事实,因此两个 PositiveReal
无法相互比较,即使它们实现了 Comparable<Real>
,没有不必要的限制 Comparable<PositiveReal>
.
总而言之:通过声明 Function<? super T, ? extends U>
,Weight extends Function<PhysicalObject, PositiveReal>
可以替代 Function<? super Car, ? extends Real>
以使用 Comparable<Real>
比较 Car
。
我希望这个简单的例子可以阐明为什么这样的声明是有用的。
代码:省略 ? extends
或 ? super
时的后果的完整列举
这是一个可编译的示例,其中系统地列举了如果我们省略 ? super
或 ? extends
可能出错的所有事情。此外,还显示了两个(丑陋的)部分 work-arounds。
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);
}
}
相关链接
- Scala中definition-site协变和逆变的详解:
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));
}
我明白 super
和 extends
之间的区别。我不明白的是为什么这种方法有它们。有人可以给我一个例子,当参数看起来像这样 Function<T, U> keyExtractor
时无法实现什么?
例如:
Comparator<Employee> employeeNameComparator = Comparator.comparing(Employee::getName);
也可以用下面的函数定义编译
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));
}
比方说,我们想比较商业航班使用的飞机。因此,我们需要一种方法来进行飞行,并且 returns 一架飞机:
Plane func (CommercialFlight)
当然也就是一个Function<CommercialFlight, Plane>
.
现在,重要的是函数returns一个Plane
。返回什么样的飞机并不重要。所以像这样的方法也应该有效:
CivilianPlane func (CommercialFlight)
从技术上讲,这是一个 Function<CommercialFlight, CivilianPlane>
,它与 Function<CommercialFlight, Plane>. So without the
extends 不同,不允许使用此功能。
同样,另一个重要的事情是可以接受 CommercialFlight
作为参数。所以像这样的方法也应该有效:
Plane func (Flight)
从技术上讲,这是 Function<Flight, Plane>
,也不同于 Function<CommercialFlight, Plane>
。所以如果没有 super
,这个函数也不会被允许。
这是一个简单的例子:按重量比较汽车。我将首先描述 text-form 中的问题,然后演示如果省略 ? extends
或 ? super
中的每一个可能出错的方式。我还展示了在每种情况下都可用的丑陋的部分解决方法。 如果你更喜欢代码而不是散文,直接跳到第二部分,应该是self-explanatory。
问题的非正式讨论
首先,逆变? super T
.
假设您有两个 类 Car
和 PhysicalObject
使得 Car extends PhysicalObject
。现在假设您有一个扩展 Function<PhysicalObject, Double>
.
Weight
如果声明是 Function<T,U>
,那么你不能重用函数 Weight extends Function<PhysicalObject, Double>
来比较两辆车,因为 Function<PhysicalObject, Double>
不符合 Function<Car, Double>
。但是您显然 想要 能够按重量比较汽车。因此,逆变? super T
是有道理的,所以Function<PhysicalObject, Double>
符合Function<? super Car, Double>
.
现在是协变 ? extends U
声明。
假设你有两个类Real
和PositiveReal
使得PositiveReal extends Real
,并且进一步假设Real
是Comparable
。
假设上一个示例中的函数 Weight
实际上具有稍微更精确的类型 Weight extends Function<PhysicalObject, PositiveReal>
。如果 keyExtractor
的声明是 Function<? super T, U>
而不是 Function<? super T, ? extends U>
,你将无法利用 PositiveReal
也是 Real
的事实,因此两个 PositiveReal
无法相互比较,即使它们实现了 Comparable<Real>
,没有不必要的限制 Comparable<PositiveReal>
.
总而言之:通过声明 Function<? super T, ? extends U>
,Weight extends Function<PhysicalObject, PositiveReal>
可以替代 Function<? super Car, ? extends Real>
以使用 Comparable<Real>
比较 Car
。
我希望这个简单的例子可以阐明为什么这样的声明是有用的。
代码:省略 ? extends
或 ? super
时的后果的完整列举
这是一个可编译的示例,其中系统地列举了如果我们省略 ? super
或 ? extends
可能出错的所有事情。此外,还显示了两个(丑陋的)部分 work-arounds。
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);
}
}
相关链接
- Scala中definition-site协变和逆变的详解: