当前位置: 首页 > 知识库问答 >
问题:

为什么泛型的这种使用不会引发运行时或编译时异常?

东门胤
2023-03-14

我在一个类中有一个方法,该方法具有使用泛型指定的返回类型。

public class SomeMain {

  public static void main(String[] args) {

    Foo<Integer> foo = new Foo<Integer>();
    System.out.println(foo.getFoo()); // Works, prints out "Foo"

  }

  public static class Foo<E>  {
    public E getFoo() {
      return (E) "Foo";
    }
  }
}

对于通用返回类型,我假设上面示例中的返回将评估为:

return (Integer) "Foo";  // Inconvertible types !!!

相反,String被正确返回和打印。

如果我将调用更改为:

String fooString = foo.getFoo(); // Compile error, incompatible types found
System.out.println(fooString);

我错过了什么,以帮助我理解这里发生了什么,为什么原始版本没有导致编译错误。

共有3个答案

苏鸿卓
2023-03-14

我想补充一点,在类型擦除过程中,Java编译器用< code>Object替换了未绑定的类型参数< code>E,因此< code>Foo类实际上被编译成:

public static class Foo {
    public Object getFoo() {
        return "Foo";
    }
}

这就是为什么下面的代码是有效的(不需要强制转换):

Object obj = foo.getFoo();
System.out.println(obj);

同时,下一个代码片段会产生一个编译时错误,这是意料之中的:

Foo<Integer> foo = new Foo<Integer>();
String fooString = foo.getFoo(); // you're trying to trick the compiler (unsuccessfully)
           ^
incompatible types: Integer can not be converted to String

这就是泛型的主要职责 - 编译时检查。

然而,故事还有另一个方面——执行时间。例如,如果您编写:

Integer value = foo.getFoo();

你会得到一个在运行时抛出的ClassCastException(Java编译器插入一个checkcast指令,检查foo.getFoo()的结果是否可以转换为整数)。

韦德厚
2023-03-14

System.out.println 没有采用 Integer 的重载。所以这句话:

System.out.println(foo.getFoo());

正在调用<code>System.out.println(对象)

要验证它是否会失败,请尝试:

Foo<Integer> foo = new Foo<Integer>();
Integer fooInt = foo.getFoo(); //class cast exception

以下操作将以相同的方式失败:

public static void main(String[] args) throws Exception {
    Foo<Integer> foo = new Foo<Integer>();
    print(foo.getFoo()); //Would also fail with a class cast exception
}
static void print(Integer in) {
    System.out.println(in);
}

由于显而易见的原因,这是编译失败的:

String fooString = foo.getFoo(); //can't work

< code>foo是< code>foo

尚阳炎
2023-03-14

这是因为重载解析解析解析了对<code>println(对象)。

请记住,Java的泛型在运行时被删除。像(E)"Foo"这样的强制转换被删除,并被移动到调用site。有时这是不必要的,所以只有在需要时才会强制转换为正确的类型。

换句话说,在< code>getFoo中不执行任何强制转换。语言规范支持这一点:

第 5.5.2 节 已检查的转换和未检查的转换

>

  • 演员表是完全未经检查的演员表。

    不会对此类强制转换执行任何运行时操作。

    擦除后,< code>getFoo返回< code>Object。它被传递到< code>println(Object)中,这非常好。

    如果我调用此方法并传递 foo.getFoo,我将得到一个错误:

    static void f(Integer i) {
        System.out.println(i);
    }
    // ...
    f(foo.getFoo()); // ClassCastException
    

    因为这一次需要浇铸。

  •  类似资料:
    • 问题内容: 我在类中有一个方法,该方法具有通过使用泛型指定的返回类型。 对于通用返回类型,我假设以上示例中的返回将计算为: 而是返回a并正确打印。 如果将调用更改为:我收到编译错误: 我缺少什么来帮助我了解这里发生了什么以及为什么原始版本没有导致编译错误。 问题答案: 这是因为重载解析解决了您的调用,因为没有。 请记住,Java的泛型会在运行时删除。像这样的演员阵容将被删除,并移至呼叫站点。有时这

    • 问题内容: 我正在尝试使用泛型实现以下结构。收到编译器错误,无法找出原因。 这个想法是译者使用T作为字典中键的类型。例如,可以是字符串或枚举。子类提供具体的字典。 但是它失败,因为:“类型’String’不符合协议’Hashable’” 但是String符合Hashable。它也不适用于Int,后者也符合Hashable。 如果删除类型约束,则仅用于测试(在此我还必须禁用字典,因为我不能在其中使用

    • 发生在运行时,因为编译器不能在决定执行哪个函数,但为什么编译器不能在编译时决定呢? 产出: 狗在吃...

    • 我有一个超类动物和两个子类:猫和狗。Dog类有一个专属于它自己的新方法,称为playFetch()。 在运行时,我们知道会发生什么。请说服我为什么编译器不抱怨。还是我的论点正确?

    • 我得到类型不匹配错误,直到我将代码重构为: 返回类型为 此例程末尾的流的类型为 形式类型参数具有以下定义:

    • 问题内容: 如果我创建一个不实现Comparable的任意类,并尝试将其用作树集,则在插入对象时,它将在运行时引发异常: 我不是Java专家,但是关于此的某些事情似乎以我所没有想到的方式进行了动态类型化(例如Python)。TreeSet的实现没有办法指定其泛型类型参数必须实现Comparable以便可以在编译时捕获它吗?非泛型函数可以将接口作为参数。泛型不一样吗? 问题答案: 之所以这样实现是因