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

2022-09-03 06:04:23

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

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 !!!

相反,返回并正确打印 a。String

如果我将调用更改为:

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

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


答案 1

这是因为重载解析解决了您对 的调用,因为没有 。printlnprintln(Object)println(Integer)

请记住,Java 的泛型在运行时会被擦除。像这样的演员表被删除,并被移动到呼叫站点。有时这不是必需的,因此仅在需要时才将内容转换为正确的类型。(E) "Foo"

换句话说,内部没有进行演员表。语言规范支持以下功能:getFoo

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

  • 演员阵容是一个完全不受约束的演员阵容。

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

擦除后,返回 。这被传递到 ,这完全没问题。getFooObjectprintln(Object)

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

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

因为这次需要施法。


答案 2

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

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

正在呼叫 .System.out.println(Object);

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

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

foois ,并返回 an,编译器可以拾取它。Foo<Integer>foo.getFoo()Integer


推荐