请考虑以下代码示例:
@SafeVarargs
public static <U> Object[] sortedCopy(Comparator<? super U> comparator, U... values) {
U[] copy = Arrays.copyOf(values, values.length);
Arrays.sort(copy, comparator);
return copy; //copy is implicitly cast to Object[] -> no heap pollution
}
public static <U> Object[] sortedCopy(U... values) {
return sortedCopy(Comparator.naturalOrder(), values); //why does this compile???
}
我原本希望编译器拒绝sortedCopy(U...)
中的行,其理由如下:
Comparator.naturalOrder()
的返回类型为Comparator<T>
,其中T
是此方法的泛型类型参数,必须满足约束T extends Comparable<? super T>
。由于该方法不接受任何参数,并且其在上面的代码中的调用没有明确指定T
的类型(如Comparator.<U>naturalOrder()
,因为U
不会扩展Comparable
{1}}),T
必须以另一种方式推断。我可以看到推断T
的唯一方法是使用sortedCopy(Comparator<? super U>, U...)
的方法签名。编译器知道values
的类型,因此可以推断U
,反过来也可以推断T
的边界,即有界通配符? super U
。但是,编译器应该意识到任何? super U
都不能满足T
Comparator.naturalOrder()
所指定的T extends Comparable<? super T>
的要求,因为U
本身不会扩展Comparable<? super U>
,因此U
的任何超类型都不能。
让我感到困惑的是,当我将签名从sortedCopy(U...)
更改为sortedCopy(U[])
时,编译器会生成错误。我想这与以下事实有关:在第二种情况下,U
在运行时作为数组的类型存在,而在第一种情况下,它不是。但我仍然不明白为什么这会使有问题的方法调用有效,因为:
Object[]
,如果我理解的话正确的是,上述代码就是这种情况,因为来自U
的{{1}}是不可恢复的。但即便如此,为什么编译器没有意识到sortedCopy(U...)
没有扩展Object
?Comparable<? super Object>
可能在运行时不再存在,但编译器仍然知道它并且应该能够检查是否满足等式约束,无论方法参数是数组还是vararg。那么为什么上面的代码示例中的行仍然可以编译?
除此之外,如果方法U
的{{1}}注释不合适,我也会感激您的反馈。我相信它是,但我对它没有信心。
答案 0 :(得分:3)
Varargs可能有点鬼鬼祟祟。我在IDE中看到的是它将sortedCopy(U... values)
视为递归方法,这意味着它没有选择带有Comparator
作为其第一个参数的重载参数。
如果将此值从varargs更改为数组参数,并传入int[]
,则会出现您期望的编译失败。
Error:(12, 16) no suitable method found for sortedCopy(java.util.Comparator<T>,U[])
method Foo.<U>sortedCopy(java.util.Comparator<? super U>,U[]) is not applicable
(inferred type does not conform to upper bound(s)
inferred: U
upper bound(s): java.lang.Comparable<? super U>,U,java.lang.Object)
method Foo.<U>sortedCopy(U[]) is not applicable
(cannot infer type-variable(s) U
(actual and formal argument lists differ in length))
Error:(18, 47) no suitable method found for sortedCopy(java.util.Comparator<T>,int[])
method Foo.<U>sortedCopy(java.util.Comparator<? super U>,U[]) is not applicable
(inference variable U has incompatible bounds
equality constraints: int
upper bounds: java.lang.Object)
method Foo.<U>sortedCopy(U[]) is not applicable
(cannot infer type-variable(s) U
(actual and formal argument lists differ in length))
如果您传入Integer[]
,或者仅传入vararg表单,则只会收到第一个错误。
词法分析器可以使用picking the most specific method周围的规则。具体来说,你被这条规则所灼烧:
如果S <:T
,则类型S对于任何表达式比类型T更具特异性
更详细:
类型T的子类型都是U类型,因此T是U的超类型和null类型。
由此我们推断,因为泛型没有良好的约束,它接受Comparable
作为你的vararg的一部分。这就是为什么我的IDE选择它作为一个递归方法,以及为什么当我运行它时,我得到一个StackOverflowError
。
如果您将通用版本正确绑定到<U extends Comparable<U>>
以确保它不会拾取您无法实际排序的任何内容,则此问题实际上会消失...
public static <U extends Comparable<U>> Object[] sortedCopy(Comparator<? super U> comparator, U... values) {
U[] copy = Arrays.copyOf(values, values.length);
Arrays.sort(copy, comparator);
return copy;
}
public static <U extends Comparable<U>> Object[] sortedCopy(U... values) {
return sortedCopy(Comparator.naturalOrder(), values);
}
...警告你可能现在有堆污染,而且引入固定方法会更简单,更简洁。
答案 1 :(得分:1)
在我看来,这个函数正在调用你自己的场景中调用自己。
在第二种方法中,你可以用对象替换U,一切都适合那里,因为它是一个varargs。