Java 8 Comparator比较静态函数

Jia*_* Li 7 java generics bounded-wildcard

对于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));
    }
Run Code Online (Sandbox Code Playgroud)

我明白之间的差别superextends.我不明白的是,为什么这种方法有它们.有人能举例说明参数看起来像什么时无法实现的Function<T, U> keyExtractor

例如 :

Comparator<Employee> employeeNameComparator = Comparator.comparing(Employee::getName);
Run Code Online (Sandbox Code Playgroud)

也可以使用以下函数定义进行编译

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));
}
Run Code Online (Sandbox Code Playgroud)

And*_*kin 9

这是一个简单的例子:按重量比较汽车.我将首先以文本形式描述问题,然后展示如果省略? 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声明.

假设你有两个类RealPositiveReal这样PositiveReal extends Real,而且认为RealComparable.

假设您Weight上一个示例中的函数实际上具有稍微更精确的类型Weight extends Function<PhysicalObject, PositiveReal>.如果申报keyExtractorFunction<? super T, U>的而不是Function<? super T, ? extends U>,您将无法充分利用的事实,PositiveReal也是一个Real,因此有两个PositiveReals不能互相比较,虽然它们实现Comparable<Real>,没有不必要的限制Comparable<PositiveReal>.

总结:与声明Function<? super T, ? extends U>,则Weight extends Function<PhysicalObject, PositiveReal>可以替换为一个Function<? super Car, ? extends Real>比较Car使用S 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);

  }
}
Run Code Online (Sandbox Code Playgroud)

相关链接

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


Joe*_*e C 5

比方说,例如,我们想通过他们使用的飞机来比较商业航班。因此,我们需要一个接收航班并返回飞机的方法:

Plane func (CommercialFlight)
Run Code Online (Sandbox Code Playgroud)

那当然是一个Function<CommercialFlight, Plane>

现在,重要的是该函数返回一个Plane. 返回什么样的飞机并不重要。所以像这样的方法也应该有效:

CivilianPlane func (CommercialFlight)
Run Code Online (Sandbox Code Playgroud)

现在从技术上讲,这是 a Function<CommercialFlight, CivilianPlane>,与Function<CommercialFlight, Plane>. So without theextends 不同,不允许使用此功能。

同样,另一个重要的事情是可以接受 aCommercialFlight作为参数。所以像这样的方法也应该有效:

Plane func (Flight)
Run Code Online (Sandbox Code Playgroud)

从技术上讲,这是一个Function<Flight, Plane>,它也不同于Function<CommercialFlight, Plane>。所以没有super,这个功能也不会被允许。