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

后端 未结 3 653
一整个雨季
一整个雨季 2021-01-01 18:44

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) {

             


        
相关标签:
3条回答
  • 2021-01-01 19:04

    System.out.println does not have an overload that takes Integer. So this statement:

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

    Is calling 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.

    0 讨论(0)
  • 2021-01-01 19:05

    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:

    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).

    0 讨论(0)
  • 2021-01-01 19:17

    This is because overload resolution resolved your println call to println(Object), since there is no println(Integer).

    Keep in mind that Java's generics are erased at runtime. And casts like (E) "Foo" are removed, and are moved to call site. 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. The language spec supports this:

    Section 5.5.2 Checked Casts and Unchecked Casts

    • The cast is a completely unchecked cast.

      No run-time action is performed for such a cast.

    After erasure, getFoo returns Object. And that gets passed into println(Object), which is perfectly fine.

    If I call this method and pass foo.getFoo, I will get an error:

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

    because this time it needs to be casted.

    0 讨论(0)
提交回复
热议问题