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

Cep*_*pod 8 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) {}
Run Code Online (Sandbox Code Playgroud)

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

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

顺便说一下,可以通过将第二种方法的声明改为来解决问题

<T,X> void foo(Special<X,T> s) {}
Run Code Online (Sandbox Code Playgroud)

MC *_*ror 3

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

\n\n

没有泛型

\n\n

考虑以下代码:

\n\n
interface NoGenericsBase { }\n\ninterface NoGenericsSpecial extends NoGenericsBase { }\n\ninterface NoGenericsSuperSpecial extends NoGenericsSpecial { }\n\nvoid foo(NoGenericsBase b) {\n    System.out.println("Feel tha base");\n}\n\nvoid foo(NoGenericsSpecial s) {\n    System.out.println("Special delivery");\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我们一直被教导编译器会选择最具体的方法。事实上,如果您调用 ,上述代码将foo((NoGenericsSuperSpecial) null)打印以下内容:

\n\n
\n

特快专递

\n
\n\n

到目前为止,一切都很好。

\n\n

泛型

\n\n

现在,让我们测试一些泛型行为:

\n\n
interface Base<T> { }\n\ninterface Special<T> extends Base<T> { }\n\nvoid foo(Base<? extends Number> b) {\n    System.out.println("Feel tha base");\n}\n\nvoid foo(Special<? extends Number> s) {\n    System.out.println("Special delivery");\n}\n\npublic static void main(String[] args) {\n    Special<Integer> v = null;\n    new Main().foo(v);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这段代码可以编译。编译器发现两个匹配项 \xe2\x80\x93 都Base<? extends Number>适用于Special<? extends Number>\xe2\x80\x93,但编译器能够找出哪个是最具体的:它将选择void foo(Special<? extends Number>),因为未绑定通配符的两个捕获是相等的。

\n\n

但是让我们重写foo(Base<...>)方法并保留其余部分不变:

\n\n
void foo(Base<? extends Integer> b) {\n    System.out.println("Feel tha base");\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在出现以下错误:

\n\n
\n

对 foo 的引用不明确
\n 方法foo(Base<? extends Integer>)和方法都foo(Special<? extends Number>)匹配

\n
\n\n

在找出最具体的类型匹配之前,编译器会处理类型变量。显然,无论变量本身(或)的类型如何,编译器都无法确定是否适用<? extends Number><? extends Integer>适用。BaseSpecial

\n\n

看来变量类型处理优先于有关继承的方法签名的选择。

\n\n

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

\n\n

原因

\n\n

我不知道编译器是否无法选择有关泛型的最具体的一个,或者它没有配置为这样做。

\n\n

有关更多详细信息,请参阅 Java 语言规范\xc2\xa7 18.5\xc2\xa7 4.5.1 。

\n\n

让我花一些时间阅读更多泛型,这样也许我们可以弄清楚......

\n