微技术

关注技术领域,分享资讯和经验。

关于泛型:Java 8比较器比较静态函数

Java 8 Comparator comparing static function

对于Comparator类中的比较源代码

1
2
3
4
5
6
    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));
    }

我了解superextends之间的区别。 我不明白的是,为什么这种方法有他们。 有人可以给我一个例子,说明参数看起来像Function keyExtractor不能实现的事情吗?

例如 :

1
Comparator<Employee> employeeNameComparator = Comparator.comparing(Employee::getName);

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

1
2
3
4
5
6
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));
}
 相关讨论

  • 如所写,这是边缘问题,要求提供示例/建议。 也许您可以在更多背景下重新表述该问题,以了解您想要达到的目标以及确切的混淆之处,也许您尝试过的例子不起作用?
  • 您能否提供有关返回时使用的强制转换(Comparator < T >和可序列化)的更多说明? 他们是什么意思?


这是一个简单的示例:按重量比较汽车。我将首先以文本形式描述该问题,然后演示各种可能的方式,如果省略? extends? super会出现错误。我还将展示在每种情况下都可用的丑陋的部分解决方法。如果您喜欢代码而不是散文,请直接跳至第二部分,这应该是不言自明的。

对问题的非正式讨论

首先,反变量? super T

假设您有两个类CarPhysicalObject,例如Car extends PhysicalObject。现在假设您有一个扩展Function的函数Weight

如果声明为Function,则您将无法重用函数Weight extends Function来比较两辆车,因为Function将不符合Function。但是您显然希望能够通过重量比较汽车。因此,反变量? super T是有意义的,因此Function符合Function

现在,协变? extends U声明。

假设您有两个类RealPositiveReal,例如PositiveReal extends Real,并且还假定RealComparable

假设上一个示例中的函数Weight实际上具有稍微更精确的类型Weight extends Function。如果keyExtractor的声明是Function而不是Function,则您将无法利用PositiveReal也是Real的事实,因此两个PositiveReal不能即使它们实现了Comparable,也可以相互比较,而没有不必要的限制Comparable

总结一下:使用声明Function,可以用Weight extends Function代替Function,以便使用Comparable比较Car

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

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

这是一个可编译的示例,其中对如果省略? super? extends可能会出错的所有事物进行系统的枚举。此外,显示了两个(难看的)部分解决方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import java.util.function.Function;
import java.util.Comparator;

class HypotheticComparators {

  public static <A, B> Comparator<A> badCompare1(Function<A, B> f, Comparator 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 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 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 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(> 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
    // 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
    // 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);

  }
}