15

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");

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

疯狂的猜测:“捕获#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);
    }
}
4

1 回答 1

12

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

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

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

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

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

如果您真的想要推断,一种可能的解决方法是执行以下操作:

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

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

于 2009-08-18T15:08:20.730 回答