Java raw type value assigned to generic type run time getClss() method error

倾然丶 夕夏残阳落幕 提交于 2019-12-23 10:59:41

问题


public class Box<T> {
    private T t;
    public Box(T t){
        this.t = t;
    }
    public void add(T t) {
      this.t = t;
    }
    public T get() {
      return t;
    }
    public static void main(String[] args) {
      Box<Integer> b = new Box(new String("may be"));
      System.out.println(b.get()); // successfully print out "may be"
      System.out.println(b.get().getClass()); // error
   }
}

This code gives a runtime error:

exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
  1. why is b.get() not triggering a runtime error?
  2. why does an runtime error occur only when I try to get the class of the class variable?

To be more precise: why is the compiler inserting a checkcast instruction into the bytecode only for the second get() (leading to the exception)?


回答1:


Please note:

  • in the first case, the result of get() is used for println(Object): in other words: the receiving side expects an Object, and that "condition" will always be true.
  • in the second case, a method invocation on the returned object will follow. And now it makes can make a huge difference if the returned type is the expected one. Therefore the compiler is adding this check to guarantee that the following method call is sound.

As background, one can look into the Java Language Specification, chapter 5.52:

The cast is a checked cast.

Such a cast requires a run-time validity check. If the value at run time is null, then the cast is allowed. Otherwise, let R be the class of the object referred to by the run-time reference value, and let T be the erasure (§4.6) of the type named in the cast operator. A cast conversion must check, at run time, that the class R is assignment compatible with the type T, via the algorithm in §5.5.3.

respectively chapter 5.53 Checked Casts at Run Time.




回答2:


This variable declaration is inconsistent.

  Box<Integer> b = new Box(new String("may be")); :

The instantiated object is raw, so the compiler emits a warning about it but allow to assign the raw type to a generic variable : Box<Integer>.

b.get() doesn't fail as you don't assign the result to a variable.
So the compiler doesn't need to cast it to anything.

Try that :

Integer value = b.get();

It will compile fine but you will get the same exception at runtime as the JVM will try to cast the value to Integer:

java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

When you invoke :

System.out.println(b.get().getClass()); // error

Things are are close to.

Why the Integer class is expected here ?

At compile time, b is declared as Box<Integer> b.
So the compiler knows that b is parameterized with a Integer type.
So yes after compilation, generics are erased but the compiler adds however some casts according to the declared code.

And this is the case here.
You invoke getClass() on a variable that is parameterized with Integer.
But Class is a generic class : Class<T>.
So a cast to Integer was added by the compiler to conform to Class<Integer>.

Of course, by using generics both for the variable declaration and instantiation :

  Box<Integer> b = new Box<>(new String("may be"));

this kind of inconsistency is not possible as the compiler stops you right now.




回答3:


Besides the answers here, the byte instruction checkcast gets called on typechecks which are not the concrete type of Object. For example

 public static void main(String[] args) {
          Box<Integer> b = new Box(new String("may be"));
          doStuff(b.get()); // no checkcast needed - works fine
          doIntegerStuff(b.get()); // run-time error with checkcast
          doStringStuff(b.get()); // compile error
       }

 public static void doStuff(Object object){}
 public static void doStringStuff(String integer){}
 public static void doIntegerStuff(Integer integer){}



回答4:


In the line of code Box<Integer> b = new Box(new String("may be")); On creation of the new Box object is missing about the type Info and assuming as a Object class for default.

Box<String> b = new Box<>(new String()); this is the right way to do so

-------- added --------

In Object class's method getClass() returns the runtime Class object. and when returning, your code Box<Integer> makes getClass() method to return Class<Integer> not Class<String>.

You gaved raw type with new B(new String()); so the compiler passed for the syntax, but while calling getClass() it casts inner object String to Integer and throws the RuntimeException



来源:https://stackoverflow.com/questions/45757141/java-raw-type-value-assigned-to-generic-type-run-time-getclss-method-error

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!