Java通配符签名

Java通配符签名,java,generics,wildcard,comparator,Java,Generics,Wildcard,Comparator,为什么宣言看起来像这样: default <U extends Comparable<? super U>> Comparator<T> thenComparing( Function<? super T, ? extends U> keyExtractor) void test() { C <Number> c = new C(); A<Integer> a

为什么宣言看起来像这样:

default <U extends Comparable<? super U>> Comparator<T> thenComparing(
            Function<? super T, ? extends U> keyExtractor)
    void test() {
        C <Number> c = new C();
        A<Integer> a = new A();
        c.parameterMethod((A<U>) a); // No compile time error cuz of cast
        int x = a.something; // doesn't issue compile time error and will cause run-time ClassCastException error
    }
默认值为什么是
?扩展U
而不是
U
? 因为代码约定。查看一个很好的解释

有什么实际的区别吗? 在正常编写代码时,编译器将为
供应商
函数TL等推断出正确的
T
;博士

Comparator.ThenComparating(函数keyExtractor)
(您的问题专门询问的方法)可能会被声明为惯用的/内部编码约定,JDK开发团队出于整个API一致性的原因必须遵循


冗长的版本


·…但我不明白这一部分:
Function您的问题似乎与类型参数有关,因此为了简单起见,我将在回答中将您提供的类型参数与它们所属的类型分开

首先,我们应该注意,参数化类型的通配符无法访问其属于相应类型参数的成员。这就是为什么在您的特定情况下,
?扩展U
可以替换为
U
,仍然可以正常工作

这不会在所有情况下都起作用。类型参数
U
没有
所具有的多功能性和额外的类型安全性?扩展U
has。通配符是一种唯一的类型参数,其中参数化类型(带有通配符类型参数)的实例化不受类型参数的限制,因为如果类型参数是具体类型或类型参数,则不会受到类型参数的限制;通配符基本上是比类型参数和具体类型(用作类型参数时)更通用的占位符。内容如下:

在泛型代码中,被称为通配符的问号(?)表示未知类型

为了说明这一点,我们来看看这个

class A <T> {}
在parameterMethod()中使用类型参数的危险在于,类型参数可以以强制转换的形式引用,从而允许访问
something
成员

class C<U> {
    parameterMethod(A<U> a) { a.something = (U) "Hi"; }
}

因此,由于类型参数可以以强制转换的形式引用,因此从声明类的类型参数中向具有类型参数参数或包含类型参数的方法传递对象是非法的。通配符不能以强制转换的形式引用,因此
wildMethod中的
A
(ADelfikPro的回答很有意义。我会再等一会儿,如果没有其他人插话,我会接受他的回答“我会再等一会儿…”…“–@kng-«一点»有多长?我有一些想法想和大家分享。但我现在不能把它打印出来。在我投入工作之前,最好知道如果我晚了X分钟,这不会浪费时间。当然没问题,去做吧@REDUP将很高兴详细说明最后一句话。我想这与否有关如果我只传递了一个匹配的方法引用(没有显式声明),他会键入
U
以正确推断为
A
,对吗?确切地说,编译器将为您推断出正确的泛型类型,无需显式声明。这都是正确的,但不适用于此问题中的情况。显式地为方法提供泛型类型参数,然后为函数使用另一种类型是没有意义的,即我找到了
Comparator.comparing(钥匙拔出器)
示例很复杂。您可以使用
函数keyExtractor
调用该方法,它会编译得很好。这只是一个编码风格的问题,没有其他问题。您正在回答这个问题,DelfikPro:«如果调用方调用的方法的签名与声明的签名不同,则l编译失败?»但我认为@kng并不是真的在问这个问题。我解释真正被问到的问题的方式更像:«如果
keydextractor
被声明为返回
U
,而不是
,那么扩展U
,会
然后比较()
仍然与最初的行为完全相同?但是,如果我误解了你,请纠正我,kng?TIA。@Duplicater,我相信,你回答了“为什么jdk开发人员以这种方式编写此代码?”的问题,我回答了“实际上有什么区别,如果有,是什么区别?”这是绝对正确的。在这种特定情况下,让
扩展U
或只是
U
在第19行(而不是第27行)注释
?扩展
,或者在
然后比较(…)
方法之前放置显式泛型(您这样做只是为了
比较(…)
)你会得到一大堆编译错误。@DelfikPro我在做这个实验时假设,
keyExtractor
会根据其返回类型表现出不同的行为。就功能而言,没有什么比
thenCompare()更好的了
可以处理返回的
?扩展U
,而它不能处理
U
。从方法签名的角度来看,是的,
U
?扩展U
的区别确实会对调用方产生影响。但是,在方法内部,只要该方法可以处理
U
,它就可以处理从调用方返回的内容
keyExtractor
,无论
keyExtractor
是返回
U
还是
?扩展U
,都无关紧要。我知道这没有实际的区别,但对于
comparing()
而不是
thencaring(),您似乎需要巧妙地或不需要明确地指定泛型类型
“对于您来说,为
比较()
而不是为
然后比较()
”–@DelfikPro-Zero(
0
)明确指定泛型类型似乎是巧妙的,或者是不必要的。
class A <T> {}
A <Number> aConcrete = new A <Integer>(); // Compile time error
A <? extends Number> aWild = new A<Integer>() // Works fine
class C <U> {
    void parameterMethod(A<U> a) {}
    void wildMethod(A<? extends U> a) {}
    void test() {
        C <Number> c = new C();
        A<Integer> a = new A();
        c.parameterMethod(a); // Compile time error
        c.wildMethod(a); // Works fine
    }
class A<T> { T something; }
class C<U> {
    parameterMethod(A<U> a) { a.something = (U) "Hi"; }
}
    void test() {
        C <Number> c = new C();
        A<Integer> a = new A();
        c.parameterMethod(a); // Compile time error
        c.wildMethod(a); // Works fine
    }
    void test() {
        C <Number> c = new C();
        A<Integer> a = new A();
        c.parameterMethod((A<U>) a); // No compile time error cuz of cast
        int x = a.something; // doesn't issue compile time error and will cause run-time ClassCastException error
    }