I was playing around with Java today, and I noticed something weird. Consider this code:
String foo = cast("hi");
int bar = cast("1");
The cast() method is here:
public static T cast(Object value) {
return (T) value;
}
Java seems to cast "hi" to a String, even if I didn't pass any hint that it would be a String, with the exception of the type. It does foo well, but fails on bar, because you can't case a String to an Integer. What is happening here?
I have two guesses:
The cast method is returning an Object, and at initialization it automatically casts to the type.
This doesn't make sense, as it would give me:
Type mismatch: cannot convert from Object to int
Java seems to know that it needs to cast to String, or whatever the type of the variable is. But how does this work?
解决方案
Your cast method does an unchecked conversion, which is handled specially in the JVM to maintain backward compatibility with non-generic code.
Such calls cannot be shown to be statically safe under the type system using generics. Rejecting such calls would invalidate large bodies of existing code, and prevent them from using newer versions of the libraries. JLS 5.1.9
Calling the method without explicit type parameters will cause the compiler to infer the type parameter of the invocations, in this case based on their expected return type. Type Inference, JLS 15.12.2.7.. This means that code is equvivalent to this:
String foo = Caster.cast("hi"); // no exception
int bar = Caster.cast("1"); // runtime ClassCastException
Primitive types will inferred to their boxed version:
If A is a primitive type, then A is converted to a reference type U via boxing conversion and this algorithm is applied recursively to the constraint U << F. JLS 15.12.2.7.
The JVM ensures type safety by doing runtime type checks on return values of functions containing unchecked casts, at the first point where the type information is not erased (I didn't find it explicitly stated in the specification, but things look to work this way, although it is mentioned in The Java Tutorials). In this case, where you try to assign the value to a typed local variable, the type of the return value is checked, resulting in a ClassCastException.
To give some more idea when that enforced runtime type checking cast happens, here are a few more examples:
Object a3 = Caster.cast(3); // no exception, a3 is now Integer
Object a4 = (String)Caster.cast(3); // an explicit cast causes runtime ClassCastException
EDIT:
Here is a StackOverflow question about when are runtime type checks enforced: When is generic return value of function casted after type erasure?