Java 8 Comparator 比较静态函数

问题描述 投票:0回答:3

Comparator类中的比较源代码

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));
}
java generics bounded-wildcard
3个回答
16
投票

这是一个简单的例子:按重量比较汽车。我将首先以文本形式描述问题,然后演示如果省略

? extends
? super
会如何出错。我还展示了在每种情况下都可用的丑陋的部分解决方法。 如果你更喜欢代码而不是散文,请直接跳到第二部分,它应该是不言自明的。


问题的非正式讨论

首先,逆变

? super T

假设您有两个类

Car
PhysicalObject
,使得
Car extends PhysicalObject
。现在假设您有一个扩展
Weight
的函数
Function<PhysicalObject, Double>

如果声明是

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>
,以使用
Car
来比较
Comparable<Real>

我希望这个简单的例子能够阐明为什么这样的声明是有用的。


代码:完整枚举省略
? extends
? super
时的后果

这是一个可编译的示例,系统地枚举了如果我们省略

? super
? extends
可能会出错的所有事情。此外,还显示了两个(丑陋的)部分解决方法。

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);

  }
}

相关链接

  1. Scala 中定义位协变和逆变的详细说明:如何检查函数中元素的协变和逆变位置?

6
投票

举例来说,我们想根据商业航班使用的飞机进行比较。 因此,我们需要一个接收航班并返回飞机的方法:

Plane func (CommercialFlight)

那当然是

Function<CommercialFlight, Plane>

现在,重要的是该函数返回一个

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
,这个功能也不会被允许。


0
投票

有人能给我一个例子,说明当参数看起来像这样时无法实现什么吗

Function<T, U> keyExtractor

给出以下类和接口来说明示例:

interface Vehicle { Engine getEngine(); }

class Car implements Vehicle { public Engine getEngine(){ return new CombustionEngine(); } }

abstract class Engine implements Comparable<Engine> {
    int power; public int compareTo(Engine o){ return Integer.compare(power, o.power); }
}

class CombustionEngine extends Engine {}

如果
? super T
只是
T
的话,什么是无法实现的?

假设我为

keyExtractor
实现了
Vehicle
函数,并且我需要为
Comparator
实现
Car

Function<Vehicle, Engine> extractor = v -> v.getEngine();
Comparator<Car> comparator = comparing(extractor);

此处

T
必须推断为
Car
,因为我们想要一个
Comparator<Car>
,但我们将
Function<Vehicle, Engine>
传递给
comparing
,并且由于
Car
!=
Vehicle
,代码将无法编译,并出现以下错误:

error: incompatible types: inference variable T has incompatible equality constraints Car,Vehicle

具有功能<?超级T,? extends U> keyExtractor 解决了这个问题,因为

Vehicle
Car
的超类,所以编译器对此感到满意。

如果
? extends U
只是
U
就无法实现什么?

假设我们在调用

comparing
时显式地编写泛型类型(例如出于代码约定的目的):

Function<Vehicle, CombustionEngine> combustionExtractor = v -> (CombustionEngine)v.getEngine();
Comparator.<Car, Engine>comparing(combustionExtractor);

这里我们明确指出

U
必须被推断为
Engine
但我们将
Function<Vehicle, CombustionEngine>
传递给
comparing
并且由于
Engine
!=
CombustionEngine
代码将无法编译并给出以下错误:

error: incompatible types: Function<Vehicle,CombustionEngine> cannot be converted to Function<? super Car,Engine>

具有功能? extends U> keyExtractor 解决了这个问题,因为

CombustionEngine
Engine
的子类,所以编译器对此感到满意。

© www.soinside.com 2019 - 2024. All rights reserved.