返回类型中推断的通配符泛型

2022-09-03 15:17:50

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

举个例子:我有一个泛型类,它只存储一对值,可以按以下方式使用:Pair<T1, T2>

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

该方法如下所示:of

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

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

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

(请注意显式强制转换以生成正确的类型。List.class

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

类型不匹配:无法从 转换为TestClass.Pair<Class<capture#1-of ?>,String>TestClass.Pair<Class<?>,String>

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

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

有人可以解释这种行为吗?这是设计使然吗?是想要的吗?是我做错了什么,还是偶然发现了编译器中的设计缺陷/错误?

疯狂的猜测:“capture#1-of?”似乎以某种方式暗示通配符是由编译器动态填充的,使类型为a,因此转换失败(从到)。这是对的吗?有没有办法解决这个问题?Class<List>Pair<Class<?>, String>Pair<Class<List>, String>


为了完整起见,下面是该类的简化版本:Pair

public final class Pair<T1, T2> {
    public final T1 first;
    public final T2 second;

    public Pair(T1 first, T2 second) {
        this.first = first;
        this.second = second;
    }

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

答案 1

构造函数工作的原因是显式指定类型参数。如果您这样做,静态方法也将起作用:

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

当然,你首先拥有静态方法的全部原因可能只是为了获得类型推断(这根本不适用于构造函数)。

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

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

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

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

该变量将具有更广泛的类型,并且它确实意味着在变量的类型名称中键入更多内容,但至少您不再需要在方法调用中强制转换。pair


答案 2

推荐