泛型与& -operator和命令的含糊不清

时间:2014-01-15 16:11:36

标签: java generics

我有一个奇怪的Java泛型模糊行为,我无法解释:

课堂上的3个方法:

public static <E extends ClassA & ClassB> void method(E val) {}
public static <E extends ClassC & ClassB & ClassA> void method(E val) {}
public static <E extends ClassB> void method(E val) {}

编译好。

但那些没有(歧义违规):

public static <E extends ClassA & ClassB> void method(E val) {}
public static <E extends ClassB & ClassC & ClassA> void method(E val) {}
public static <E extends ClassB> void method(E val) {}

(ClassA,ClassB,ClassC都是完全独立的接口!)

2 个答案:

答案 0 :(得分:12)

由于类型擦除,编译器需要为编译方法中的参数类型选择静态已知类型。

为此,它使用约束列表中的第一个类型。

在第一个示例中,这会为每个方法生成一个唯一类型,因此它会编译为

public static method(ClassA val);
public static method(ClassC val);
public static method(ClassB val);

这是完全合法的(除了您遗漏的退货类型);它使用三种不同的参数类型创建三个重载。

在你的第二个例子中,这会产生歧义:

public static method(ClassA val);
public static method(ClassB val);
public static method(ClassB val);

这不合法,因为最后两种方法具有相同的签名。

规范explicitly documents这种行为。

这可以通过尝试从每个重载中选择单个约束类型来使其成为合法的,这样就不存在冲突,但这将是复杂的。较大的约束列表较慢。
该规范可能会说:

  

如果它用于擦除参数列表中的类型,则选择在通用方法中擦除类型变量,使得该方法的每次重载在擦除后产生唯一的签名。
  如果没有擦除组合会产生唯一签名,则会出现歧义错误。

我怀疑这个问题出现在NP中。

答案 1 :(得分:10)

它在JLS #4.6

中定义
  

类型变量的擦除是其最左边界的擦除。

if two methods have the same erasure, the compiler gives you an error