与 &-运算符和顺序的泛型歧义

2022-09-04 01:33:24

我有一个奇怪的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) {}

(A类,B类,类C都是完全独立的接口!


答案 1

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

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

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

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);

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

规范明确记录了此行为。

这可以通过尝试从每个重载中选择一个约束类型来使其合法,这样就不会有冲突,但对于较大的约束列表,这将是复杂和缓慢的。
规范可以这样说:

如果将其用于擦除参数列表中的类型,则选择在泛型方法中擦除类型变量,以便该方法的每个重载在擦除后产生唯一的签名。
如果没有擦除组合将产生唯一签名,则会发生歧义错误。

我怀疑这个问题在NP中。


答案 2

它在 JLS #4.6 中定义:

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

如果两种方法具有相同的擦除,编译器会给您一个错误


推荐