当前位置: 首页 > 面试题库 >

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

尹承泽
2023-03-14
问题内容

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的?” 某种程度上似乎暗示着通配符是由编译器即时填充的,使得类型为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);
    }
}

问题答案:

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

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将具有更宽的类型,这的确意味着要在变量的类型名称中键入更多内容,但至少您无需再强制进行方法调用。



 类似资料:
  • 问题内容: 我了解编译器使用目标类型来确定使通用方法调用适用的类型参数。例如,在以下语句中: 其中的签名中具有类型参数 在这种情况下,推断出的类型参数是。 现在考虑以下几点: 在这种情况下,推断的类型是什么?是吗 还是因为通配符告诉编译器任何类型都是可能的? 问题答案: 通配符的每种用法都有与之关联的不同类型。(通常,JLS将此称为“新鲜类型”。)例如,这就是这样的编译器错误的工作方式: 因为在这

  • 我知道编译器使用目标类型来确定使泛型方法调用适用的类型参数。例如,在以下声明中: 其中

  • 问题内容: 我在很多地方都读过,包括在方法返回类型中使用有界通配符是一个坏主意。但是,我在课堂上找不到避免这种情况的方法。我想念什么吗? 情况看起来像这样: 总而言之,这是一个我希望能够 使用 任何与英语有所不同的出版物的类。该类需要允许从外部访问发布,但理想情况下,的调用者不希望将结果用作有界通配符。他们会很高兴的。 有办法解决吗? 问题答案: 有界通配符具有传染性,这就是您链接到的页面似乎在哀

  • 此外,是否可以说泛型通配符类型仅在方法的参数声明中才有意义?

  • 问题内容: 我试图理解Java泛型,它们似乎很难理解。例如,这很好… …就是这个… … 还有这个 … …但是不能编译: 有人可以用简单的语言解释发生了什么吗? 问题答案: 对于泛型类型,主要要了解的是它们不是协变的。 因此,尽管您可以这样做: 以下内容将无法编译: 这是为了避免您绕过通用类型的情况: 因此,一一讲解您的示例 1个 您的通用方法采用a ,而您采用;(基本上是)。可以分配给类型,并且编

  • 对于一堆带有泛型的包装类,我的继承结构遇到了一些麻烦。这基本上是结构: 这将与当前代码一起编译和工作,但是,这似乎很危险。如果调用方像这样使用这个方法:,它将很好地编译,但是如果findMiddle尝试返回SubBWrapper,则在运行时会有一个ClassCastException。我以为行得通却行不通的是: 所以我的问题基本上是,有没有正确的方法来编写编译、运行并遵循最佳实践的方法findMi