问题描述
对于Comparator类中的比较源代码
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));
}
我了解 super $ c之间的区别$ c>和
扩展
。我不明白的是,为什么这种方法有他们。有人可以给我一个例子,说明参数看起来像 Function< T,U& keyExtractor
?
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
?
例如:
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));
}
推荐答案
下面是一个简单的示例:按重量比较汽车。我将首先以文本形式描述该问题,然后演示各种可能的方法,如果怎么会出错?扩展
或? super
被省略。我还将展示在每种情况下都可用的丑陋的部分解决方法。 如果您喜欢代码而不是散文,请直接跳至第二部分,这应该是不言自明的。
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.
首先,对变量?超级T
。
假设您有两个类,分别是 Car
和 PhysicalObject
,这样 Car扩展了PhysicalObject
。现在假设您有一个扩展 Weight
的函数 Function< PhysicalObject,Double>
。
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>
.
如果声明是 Function< T,U>
,那么您将无法重用函数 Weight扩展Function< PhysicalObject,Double>
来比较两辆车,因为 Function< PhysicalObject,Double>
不符合 Function< Car, Double>
。但是您显然想要能够根据汽车的重量进行比较。因此,反变量?超级T
是有意义的,因此 Function< PhysicalObject,Double>
符合 Function< ;?超级汽车,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>
.
现在协变量了吗?扩展了U
声明。
假设您有两个类,分别是 Real
和 PositiveReal
,这样 PositiveReal扩展了Real
,并进一步假定 Real
是可比较
。
Suppose that you have two classes Real
and PositiveReal
such that PositiveReal extends Real
, and furthermore assume that Real
is Comparable
.
假设您的函数 Weight
示例实际上具有稍微更精确的类型 Weight扩展了Function< PhysicalObject,PositiveReal>
。如果 keyExtractor
的声明是 Function< ;?超级T,U>
而不是 Function< ;?超级T ,?扩展U>
,您将无法利用 PositiveReal
也是 Real 的事实code>,因此两个
PositiveReal
不能相互比较,即使它们实现了 Comparable< Real>
,而没有不必要的限制 Comparable< PositiveReal>
。
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>
.
总结:带有声明 Function< ;?超级T ,?扩展U>
, Weight扩展Function< PhysicalObject,PositiveReal>
可以代替 Function< ;?超级跑车?扩展Real>
以使用 Comparable< Real>
比较 Car
。
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.
这里是一个可编译的示例,其中系统地枚举了所有可能会出错的事物,如果我们省略?超级
或?扩展
。此外,还显示了两个(难看的)部分解决方法。
Here 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);
}
}
相关链接
- Scala中定义站点协方差和协方差的详细图示:
- Detailed illustration of definition-site covariance and contravariance in Scala: How to check covariant and contravariant position of an element in the function?
这篇关于Java 8比较器比较静态功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!