推断的返回类型的通配符generics

Java通常可以根据参数推断generics(甚至在返回类型上,与例如C#相反)。

举个例子:我有一个generics类Pair ,它只存储一对值,可以按以下方式使用:

 Pair pair = Pair.of("Hello", "World"); 

看起来像这样的方法:

 public static  Pair of(T1 first, T2 second) { return new Pair(first, second); } 

非常好。 但是,这不再适用于以下需要使用通配符的用例:

 Pair<Class, String> pair = Pair.of((Class) List.class, "hello"); 

(注意显式List.class以使List.class成为正确的类型。)

代码失败,出现以下错误(由Eclipse提供):

类型不匹配:无法从TestClass.Pair<Class,String>TestClass.Pair<Class,String>

但是,显式调用构造函数仍然按预期工作:

 Pair<Class, String> pair = new Pair<Class, String>((Class) List.class, "hello"); 

有人可以解释这种行为吗? 它是按设计的吗? 它需要吗? 我做错了什么或者我偶然发现编译器中的设计/错误存在缺陷?

狂野猜测:“捕获#1-of?”似乎暗示通配符由动态编译器填充,使类型成为Class ,从而使转换失败(来自Pair<Class, String> Pair<Class, String> )。 这是正确的吗? 有办法解决这个问题吗?


为了完整起见,这里是Pair类的简化版本:

 public final class Pair { public final T1 first; public final T2 second; public Pair(T1 first, T2 second) { this.first = first; this.second = second; } public static  Pair of(T1 first, T2 second) { return new Pair(first, second); } } 

构造函数工作的原因是您明确指定了类型参数。 如果你这样做,静态方法也会起作用:

 Pair, String> pair = Pair., String>of(List.class, "hello"); 

当然,首先你有一个静态方法的原因可能只是得到类型推断(根本不适用于构造函数)。

这里的问题(如你所建议的)是编译器正在执行捕获转换 。 我相信这是[JLS的§15.12.2.6]的结果 :

  • 所选方法的结果类型确定如下:
    • 如果声明的方法声明的返回类型为void,则结果为void。
    • 否则,如果方法需要未经检查的转换,那么结果类型是方法声明的返回类型的擦除(第4.6节)。
    • 否则,如果被调用的方法是通用的,那么对于1in,让Fi成为方法的forms类型参数,让Ai成为方法调用推断的实际类型参数,并且让R成为方法的声明返回类型调用。 通过将捕获转换(第5.1.10节)应用于R [F1:= A1,…,Fn:= An]来获得结果类型。
    • 否则,通过将捕获转换(第5.1.10节)应用于方法声明中给出的类型来获取结果类型。

如果你真的想要推理,一种可能的解决方法是做这样的事情:

 Pair, String> pair = Pair.of(List.class, "hello"); 

变量pair将具有更宽的类型,并且它确实意味着在变量的类型名称中输入更多,但至少您不再需要在方法调用中进行转换。