简体   繁体   English

为什么使用泛型不会抛出运行时或编译时异常?

[英]Why does this use of Generics not throw a runtime or compile time exception?

I've got a method in a class that has a return type specified by use of a generic. 我在类中有一个方法,它具有通过使用泛型指定的返回类型。

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

With the generic return type, I assumed the return in the above example would evaluate to: 使用泛型返回类型,我假设上面示例中的返回值将评估为:

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

Instead a String is returned and printed correctly. 而是返回并正确打印String

I get a compilation error if I change the call to be: 如果我将调用更改为:我收到编译错误:

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

What am I missing to help me understand what's going on here and why the original version didn't result in a compilation error. 我错过了什么来帮助我理解这里发生的事情以及为什么原始版本不会导致编译错误。

This is because overload resolution resolved your println call to println(Object) , since there is no println(Integer) . 这是因为重载决议解决了对println(Object) println调用,因为没有println(Integer)

Keep in mind that Java's generics are erased at runtime. 请记住,Java的泛型在运行时会被删除。 And casts like (E) "Foo" are removed, and are moved to call site. (E) "Foo"这样的演员表被删除,并被移动到呼叫站点。 Sometimes this is not necessary, so things are casted to the right type only when needed. 有时这不是必需的,因此只有在需要时才会将事物转换为正确的类型。

In other words, no casts are performed inside getFoo . 换句话说,在getFoo没有执行强制转换。 The language spec supports this: 语言规范支持:

Section 5.5.2 Checked Casts and Unchecked Casts 第5.5.2节Checked Casts和Unchecked Casts

  • The cast is a completely unchecked cast. 演员阵容完全不受限制。

    No run-time action is performed for such a cast. 没有对这样的演员表执行运行时动作。

After erasure, getFoo returns Object . 擦除后, getFoo返回Object And that gets passed into println(Object) , which is perfectly fine. 然后它被传递到println(Object) ,这非常好。

If I call this method and pass foo.getFoo , I will get an error: 如果我调用此方法并传递foo.getFoo ,我收到一个错误:

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

because this time it needs to be casted. 因为这次需要进行铸造。

System.out.println does not have an overload that takes Integer . System.out.println没有带Integer的重载。 So this statement: 所以这句话:

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

Is calling System.out.println(Object); 正在调用System.out.println(Object); .

To verify that it would otherwise fail, try: 要验证它是否会失败,请尝试:

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

The following will fail in the same way: 以下将以相同的方式失败:

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

And this is failing compilation for obvious reasons: 由于显而易见的原因,这是编译失败:

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

foo is Foo<Integer> , and foo.getFoo() returns an Integer and the compiler can pick this up. fooFoo<Integer>foo.getFoo()返回一个Integer ,编译器可以选择它。

I'd like to add that during the type erasure process, the Java compiler replaces the unbounded type parameter E with Object , therefore the Foo class is actually compiled into: 我想补充一点,在类型擦除过程中,Java编译器用Object替换unbounded类型参数E ,因此Foo类实际编译为:

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

That's why the following code is valid (cast is not needed): 这就是为什么以下代码有效(不需要强制转换):

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

At the same time, the next code snippet produces a compile-time error, as expected: 同时,下一个代码片段会产生编译时错误,如预期的那样:

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

And that's the main responsibility of generics - compile-time checks. 这是泛型的主要责任 - 编译时检查。

Yet there is another side of the story - execution-time casts. 然而,故事的另一面 - 执行时间演员。 For example, if you write: 例如,如果你写:

Integer value = foo.getFoo();

you get a ClassCastException thrown at runtime (the Java compiler inserts a checkcast instruction that examines whether the result of the foo.getFoo() can be cast to Integer ). 你得到一个在运行时抛出的ClassCastException (Java编译器插入一个checkcast指令,检查foo.getFoo()的结果是否可以foo.getFoo()Integer )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM