参数中带有通配符的模糊重载泛型方法

时间:2015-03-23 13:39:15

标签: java generics overloading overload-resolution

给出以下声明

interface Base<A> { }

interface Special<A,B> extends Base<A> { }

<T> void foo(Base<T> b) {}

<T> void foo(Special<?,T> s) {}

为什么我会收到以下代码的编译错误:

Special<String, Integer> s = null;
foo(s); // error: reference to foo is ambiguous

BTW,可以通过将第二种方法的声明改为

来解决问题
<T,X> void foo(Special<X,T> s) {}

1 个答案:

答案 0 :(得分:3)

首先,这是一个非常有趣的问题。

没有泛型

请考虑以下代码:

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>是否适用,而忽略了变量本身的类型BaseSpecial)。< / p>

在选择有关继承的方法签名之前,似乎变量类型处理来自

一个(或者至少我自己)应该期望编译器选择foo(Special<? extends Number>),但事实并非如此。

原因

我不知道编译器是否无法选择有关泛型的最具体的编译器,或者它没有配置为执行此操作。

有关详细信息,请参阅Java语言规范§ 18.5§ 4.5.1

让我花一些时间阅读更多的泛型,所以也许我们可以搞清楚......