首先,一个非常有趣的问题。
没有泛型
考虑以下代码:
interface NoGenericsBase { }
interface NoGenericsSpecial extends NoGenericsBase { }
interface NoGenericsSuperSpecial extends NoGenericsSpecial { }
void foo(NoGenericsBase b) {
System.out.println("Feel tha base");
}
void foo(NoGenericsSpecial s) {
System.out.println("Special delivery");
}
我们一直被告知编译器会选择最具体的方法。事实上,如果您调用foo((NoGenericsSuperSpecial) null),上述代码将打印以下内容:
特快专递
到目前为止,一切都很好。
泛型
现在,让我们测试一些泛型行为:
interface Base<T> { }
interface Special<T> extends Base<T> { }
void foo(Base<? extends Number> b) {
System.out.println("Feel tha base");
}
void foo(Special<? extends Number> s) {
System.out.println("Special delivery");
}
public static void main(String[] args) {
Special<Integer> v = null;
new Main().foo(v);
}
此代码编译。编译器找到两个匹配项——Base<? extends Number> 和 Special<? extends Number> 都适用——但编译器能够找出哪个是最具体的:它将选择 void foo(Special<? extends Number>),因为未绑定通配符的两个捕获是相等的。
但是让我们重写foo(Base<...>) 方法,其余的保持不变:
void foo(Base<? extends Integer> b) {
System.out.println("Feel tha base");
}
现在出现以下错误:
对 foo 的引用不明确
方法foo(Base<? extends Integer>) 和方法foo(Special<? extends Number>) 都匹配
在找出最具体的类型匹配之前,编译器会处理类型变量。显然,编译器无法确定是 <? extends Number> 还是 <? extends Integer> 应用,不管变量本身的类型(Base 或 Special)。
似乎变量类型处理先于选择与继承有关的方法签名。
一个人(或至少我自己)应该期望编译器选择foo(Special<? extends Number>),但事实并非如此。
原因
我不知道编译器是否无法选择最具体的泛型,或者它没有配置为这样做。
有关详细信息,请参阅 Java 语言规范 § 18.5 或 § 4.5.1。
让我花点时间阅读更多泛型,也许我们可以弄清楚...