问题
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
- why is
b.get()
not triggering a runtime error? - 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 forprintln(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