首页 > 解决方案 > Java thenComparing通配符签名

问题描述

为什么声明看起来像这样:

default <U extends Comparable<? super U>> Comparator<T> thenComparing(
            Function<? super T, ? extends U> keyExtractor)

我明白了大部分。U只要它与自身的超类具有可比性,因此也可以与自身具有可比性,它可以是任何东西,这是有道理的。

但我不明白这部分:Function<? super T, ? extends U>

为什么不只是有:Function<? super T, U>

U 不能参数化为 keyExtractor 返回的任何内容,并且仍然扩展Comparable<? super U>相同吗?

标签: javagenericswildcardcomparator

解决方案


为什么是? extends U而不是U

因为代码约定。查看@deduper 的答案以获得很好的解释。

有什么实际区别吗?

正常编写代码时,编译器会推断出T诸如Supplier<T>and之类的正确性,因此在编写或开发 API 时Function<?, T>没有实际理由。Supplier<? extends T>Function<?, ? extends T>

但是如果我们手动指定类型会发生什么?

void test() {
    Supplier<Integer> supplier = () -> 0;

    this.strict(supplier); // OK (1)
    this.fluent(supplier); // OK

    this.<Number>strict(supplier); // compile error (2)
    this.<Number>fluent(supplier); // OK (3)
}

<T> void strict(Supplier<T>) {}
<T> void fluent(Supplier<? extends T>) {}
  1. 如您所见,strict()无需显式声明即可正常工作,因为T被推断为Integer匹配局部变量的泛型类型。

  2. 然后,当我们尝试通过Supplier<Integer>因为并且兼容时Supplier<Number>,它会中断。IntegerNumber

  3. 然后它适用于fluent()因为? extends Number并且Integer 兼容

实际上,只有当您有多个泛型类型时才会发生这种情况,需要显式指定其中一个并错误地获取另一个(Supplier一个),例如:

void test() {
    Supplier<Integer> supplier = () -> 0;
    // If one wants to specify T, then they are forced to specify U as well:
    System.out.println(this.<List<?>, Number> supplier);
    // And if U happens to be incorrent, then the code won't compile.
}

<T, U> T method(Supplier<U> supplier);

示例Comparator(原始答案)

考虑以下Comparator.comparing方法签名:

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
    Function<? super T, U> keyExtractor
)

这里还有一些测试类层次结构:

class A implements Comparable<A> {
    public int compareTo(A object) { return 0; }
}

class B extends A { }

现在让我们试试这个:

Function<Object, B> keyExtractor = null;
Comparator.<Object, A>comparing(keyExtractor); // compile error
error: incompatible types: Function<Object,B> cannot be converted to Function<? super Object,A>

推荐阅读