Java thenComparing通配符签名

kng*_*kng 12 java generics wildcard comparator

为什么声明是这样的:

default <U extends Comparable<? super U>> Comparator<T> thenComparing(
            Function<? super T, ? extends U> keyExtractor)
Run Code Online (Sandbox Code Playgroud)

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

但我没有得到这部分: Function<? super T, ? extends U>

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

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

Del*_*Pro 16

为什么是? extends U而不是U

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

有什么实际区别吗?

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

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

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>) {}
Run Code Online (Sandbox Code Playgroud)
  1. 如您所见,strict()无需显式声明即可正常工作,因为T推断为Integer匹配局部变量的泛型类型。

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

  3. 然后它与fluent()因为? extends NumberInteger 兼容的。

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

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

示例Comparator(原始答案)

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

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

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

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
    Function<? super T, U> keyExtractor
)
Run Code Online (Sandbox Code Playgroud)

现在让我们试试这个:

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

  • 这都是真实正确的,但不适用于本问题中的情况。为该方法显式提供泛型类型参数,然后使用另一种类型的函数是没有意义的,即我发现您的“Comparator.&lt;Object, A&gt;comparing(keyExtractor)”示例很复杂。您只需使用“Function&lt;Object, B&gt; keyExtractor”并使用该方法调用该方法,它就会编译得很好。这只是编码风格的问题,没有更多的内容 (2认同)

ded*_*per 9

特尔;博士

Comparator.thenComparing(Function< ? super T, ? extends U > keyExtractor)您的问题具体询问的方法可能会以这种方式声明为惯用/内部编码约定,JDK 开发团队出于整个 API 的一致性原因必须遵循该约定。


长篇大论的版本

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

该部分对必须返回的特定类型施加了限制。听起来你已经把那部分放下了。Function

然而UFunction回报不仅仅是任何旧的U。它必须具有特定属性(又名“边界”在方法的参数部分声明): <U extends Comparable<? super U>>

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

尽可能简单地说(因为我只是简单地考虑它;而不是正式地):原因是因为U? extends U.

更改Comparable< ? super U >List< ? super U >Comparator< T >Set< T >可能使你的困惑更容易推理...

default < U extends List< ? super U > > Set< T > thenComparing(
    Function< ? super T, ? extends U > keyExtractor ) {
        
    T input = …;
        
    /* Intuitively, you'd think this would be compliant; it's not! */
    /* List< ? extends U > wtf = keyExtractor.apply( input ); */
      
    /* This doesn't comply to „U extends List< ? super U >“ either */
    /* ArrayList< ? super U > key = keyExtractor.apply( input ); */
        
    /* This is compliant because key is a „List extends List< ? super U >“
     * like the method declaration requires of U 
     */
    List< ? super U > key = keyExtractor.apply( input );
        
    /* This is compliant because List< E > is a subtype of Collection< E > */
    Collection< ? super U > superKey = key;
        
    …
}
Run Code Online (Sandbox Code Playgroud)

不能U直接参数化为任何keyExtractor返回值,并且仍然扩展Comparable<? super U>吗?......

我已经通过实验确定Function< ? super T, ? extends U > keyExtractor,确实可以重构为更具限制性的, Function< ? super T, U > keyExtractor并且仍然可以编译和运行得非常好。例如,注释/取消注释我实验的/*? extends*/第 27 行,以观察所有这些调用都以任何一种方式成功……UnboundedComparator

…
Function< Object, A > aExtractor = ( obj )-> new B( );
Function< Object, B > bExtractor = ( obj )-> new B( ) ;
Function< Object, C > cExtractor = ( obj )-> new C( ) ;
        
UnboundedComparator.< Object, A >comparing( aExtractor ).thenComparing( bExtractor );
UnboundedComparator.< Object, A >comparing( bExtractor ).thenComparing( aExtractor );
UnboundedComparator.< Object, A >comparing( bExtractor ).thenComparing( bExtractor );
UnboundedComparator.< Object, B >comparing( bExtractor ).thenComparing( bExtractor );
UnboundedComparator.< Object, B >comparing( bExtractor ).thenComparing( aExtractor );
UnboundedComparator.< Object, B >comparing( bExtractor ).thenComparing( cExtractor );
…
Run Code Online (Sandbox Code Playgroud)

从技术上讲,您可以实际代码中进行等效的去边界。从简单的实验我已经做了-特别,因为这是你的问题问什么-我找不到任何实际的理由,更喜欢了。thenComparing()? extends UU

但是,当然,我还没有详尽地测试该方法的每个用例,无论有没有有界?.

如果JDK 的开发人员还没有对其进行详尽的测试,我会感到惊讶。

我的实验——我承认是有限的——让我相信,除了作为 JDK 开发团队遵循的惯用/内部编码约定之外,没有其他原因可能会以这种方式声明。Comparator.thenComparing(Function< ? super T, ? extends U > keyExtractor)

望着在JDK的代码库,这是不是不合理的假设,有人的地方已经颁布法令:«只要有一个Function< T, R >T 必须有一个下限消费者/你输入的东西R 必须有一个上限生产者/你有些东西还给你)»。

但出于显而易见的原因,U? extends U. 因此,不应期望前者可以替代后者。

应用奥卡姆剃刀 JDK 的实现者所做的详尽测试已经确定U-upper bounded 通配符对于覆盖更广泛的用例是必要的