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 的答案以获得很好的解释。
正常编写代码时,编译器会推断出正确T的Supplier<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)
如您所见,strict()无需显式声明即可正常工作,因为T推断为Integer匹配局部变量的泛型类型。
然后当我们尝试通过Supplier<Integer>asSupplier<Number>因为Integer并且Number 不兼容时它会中断。
然后它与fluent()因为? extends Number和Integer 是兼容的。
在实践中,只有当您有多个泛型类型时才会发生这种情况,需要明确指定其中一个并错误地获取另一个(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.thenComparing(Function< ? super T, ? extends U > keyExtractor)(您的问题具体询问的方法)可能会以这种方式声明为惯用/内部编码约定,JDK 开发团队出于整个 API 的一致性原因必须遵循该约定。
长篇大论的版本
„ ……但我不明白这部分:
Function<? super T, ? extends U>…… ”
该部分对必须返回的特定类型施加了限制。听起来你已经把那部分放下了。Function
然而U,Function回报不仅仅是任何旧的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 通配符对于覆盖更广泛的用例是必要的。