2017-08-06 39 views
4

考虑下面的代码示例:执法在方法普通类型等式约束参数

@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,因此Comparator.<U>naturalOrder()无法编译),因此必须以其他方式推断出T。我可以看到推断T的唯一方法是通过方法签名sortedCopy(Comparator<? super U>, U...)。编译器知道values的类型,因此可以推断出U,并且继而可以推断出边界为T,即有界通配符? super U。但是编译器应该认识到,任何? super U都不能满足由Comparator.naturalOrder()指定的T的要求,即T extends Comparable<? super T>,因为U本身不延伸Comparable<? super U>,所以U的任何超类都不能。

让我感到困惑的是编辑器确实在将签名从sortedCopy(U...)更改为sortedCopy(U[])时生成错误。我想这与第二种情况有关,U作为数组的类型在运行时存在,而在第一种情况下,它不是。但我还是不明白为什么这将使问题的有效方法调用,这是因为:

  1. 据我了解,一般类型的可变参数参数转换为Object[]如果值传递给方法因为可变参数是通用的,因此是不可确定的类型,如果我理解正确,那么在上面的代码中就是这种情况,因为sortedCopy(U...)U是不可确定的。但即使如此,为什么编译器没有意识到Object没有扩展Comparable<? super Object>
  2. 前面的参数讨论运行时类型。但是,我们仍然是预编译的,所以关于运行时类型的猜测甚至不应该在这种情况下相关,因为尽管U在运行时可能不再存在,但编译器仍然知道它,并且应该能够检查不管方法参数是一个数组还是一个可变参数,都会实现等式约束。

那么,为什么上述代码示例中的问题仍在编译?

除此之外,如果方法sortedCopy(Comparator<? super U>, U...)@SafeVarargs注释不合适,我也将非常感谢您的反馈。我相信是这样,但我对此没有信心。

回答

3

可变参数可能有点鬼鬼祟祟。我在我的IDE中看到的是,它是关于sortedCopy(U... values)作为递归方法,这意味着它不选择Comparator作为其第一个参数的重载参数。

如果将此参数从可变参数更改为数组参数并传入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[]通过,或者如果你只在可变参数的形式传递,你只得到了第一个错误。

关于picking the most specific method的规则可用于词法分析器。具体而言,你要由该规则燃烧:

A型S是比任何表达类型T更具体若S <:T.

更详细地:

T类型的子类型都是U类型,所以T是U的超类型和空类型。

由此,我们推断,因为一般不能很好地结合,它接受Comparable为您的可变参数的一部分。这就是为什么我的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); 
} 

...并警告你可能现在有堆污染,而且它会更简单,更简洁,而不是引入固定方法。

+0

如果你改变了第二个方法的名字,很明显第一个不是被'sortedCopy(Comparator.naturalOrder(),values)调用的' – Javier

+0

@Javier:我不确定你要去哪里。如果您更改方法的名称,那么不存在任何含糊之处。我的直觉就是保持意图,并用泛型来纠正问题。 – Makoto

+0

这只是另一种证明“不选择比较器作为第一个参数的重载参数”的方法。 OP提示'(比较器比较器,U ...值)'适用于实际参数'(Comparator.naturalOrder(),values)',但事实上并非如此。 – Javier

1

在我看来,这个功能是在对你很重要的场景中调用自己的。

在第二种方法中,您可以用Object替换U,并且所有内容都适合,因为它是可变参数。