I am trying to debug an issue involving a ClassCastException in Java. In the interest of solving the issue I need to know what is going on when I cast from Object to a specific type. Can anyone explain to me how the Java cast operator works at the Java level and the JVM level?
-
Is the JLS good enough?
Casting conversion is applied to the operand of a cast operator (§15.16): the type of the operand expression must be converted to the type explicitly named by the cast operator. Casting contexts allow the use of:
- an identity conversion (§5.1.1)
- a widening primitive conversion (§5.1.2)
- a narrowing primitive conversion (§5.1.3)
- a widening reference conversion (§5.1.5) optionally followed by an unchecked conversion (§5.1.9)
- a narrowing reference conversion (§5.1.6) optionally followed by an unchecked conversion
- a boxing conversion (§5.1.7)
- an unboxing conversion (§5.1.8).
Actually, maybe this part is more relevant:
The detailed rules for compile-time legality of a casting conversion of a value of compile-time reference type S to a compile-time reference type T are as follows:
- If S is a class type:
- If T is a class type, then either |S| <: |T|, or |T| <: |S|; otherwise a compile-time error occurs. Furthermore, if there exists a supertype X of T, and a supertype Y of S, such that both X and Y are provably distinct parameterized types (§4.5), and that the erasures of X and Y are the same, a compile-time error occurs.
- If T is an interface type:
- If
S is not a
finalclass (§8.1.1), then, if there exists a supertype X of T, and a supertype Y of S, such that both X and Y are provably distinct parameterized types, and that the erasures of X and Y are the same, a compile-time error occurs. Otherwise, the cast is always legal at compile time (because even if S does not implement T, a subclass of S might). - If S is a
finalclass (§8.1.1), then S must implement T, or a compile-time error occurs.
- If
S is not a
- If T is a type variable, then this algorithm is applied recursively, using the upper bound of T in place of T.
- If T is
an array type, then S must be
the class
Object, or a compile-time error occurs.
- If S is an interface type:
- If T is an array type, then T must implement S, or a compile-time error occurs.
- If T is a type that is not
final(§8.1.1), then if there exists a supertype X of T, and a supertype Y of S, such that both X and Y are provably distinct parameterized types, and that the erasures of X and Y are the same, a compile-time error occurs. Otherwise, the cast is always legal at compile time (because even if T does not implement S, a subclass of T might). - If T is
a type that is
final, then:- If S is not a parameterized type or a raw type, then T must implement S, and the cast is statically known to be correct, or a compile-time error occurs.
- Otherwise, S is either a parameterized type that is an invocation of some generic type declaration G, or a raw type corresponding to a generic type declaration G. Then there must exist a supertype X of T, such that X is an invocation of G, or a compile-time error occurs. Furthermore, if S and X are provably distinct parameterized types then a compile-time error occurs.
- If S is a type variable, then this algorithm is applied recursively, using the upper bound of S in place of S.
- If S is an array type SC[], that is, an array of components of type SC:
- If T is a
class type, then if T is not
Object, then a compile-time error occurs (becauseObjectis the only class type to which arrays can be assigned). - If T
is an interface type, then a
compile-time error occurs unless
T is the type
java.io.Serializableor the typeCloneable, the only interfaces implemented by arrays. - If T
is a type variable, then:
- If the upper
bound of T is
Objector the typejava.io.Serializableor the typeCloneable, or a type variable that S could legally be cast to by recursively applying these rules, then the cast is legal (though unchecked). - If the upper bound of T is an array type TC[], then a compile-time error occurs unless the type SC[] can be cast to TC[] by a recursive application of these compile-time rules for casting.
- Otherwise, a compile-time error occurs.
- If the upper
bound of T is
- If T is
an array type TC[], that is, an
array of components of type TC,
then a compile-time error occurs
unless one of the following is true:
- TC and SC are the same primitive type.
- TC and SC are reference types and type SC can be cast to TC by a recursive application of these compile-time rules for casting.
Perfectly clear now, isn't it? :D
In other words, this is the best I can do without knowing more details about your problem.
Paul Tomblin : What the hell has markdown done to your reference?Michael Myers : Hmm... I went and stripped out all links from the second part, but I don't know what's wrong with the first part.Michael Myers : If anybody knows why the link is acting weird, please go ahead and fix it!Paul Tomblin : Maybe it doesn't like anchor tags?Michael Myers : But there aren't any anchor tags; I stripped them all out already. -
Other useful and authoritative references are found in the Java Virtual Machine Specification, specifically §2.6.5, "Narrowing Reference Conversions", and especially the definition of the
checkcastinstruction. -
Casting asserts that the runtime type of an object is compatible with the given static type, and thus allows you to call methods of that type on the object.
Here obj is a Integer object, but only accessible though an Object reference:
Object obj = new Integer(1);Casting lets you treat it as an Integer (or some superclass of Integer) again:
System.out.println(((Integer) obj).intValue());ClassCastException occours when the static type given does not match the runtime type of the object:
System.out.println(((Float) obj).intValue()); // runtime errorYou can find the runtime type of any object by using getClass() and the various Class methods:
System.out.println(obj.getClass()); // prints "class java.lang.Integer" -
A likely cause of class cast mystifcation is that not only do the types have to match but also they must be loaded by the same classloader.
You should be able to dump not only the type hierarchy but also the identity of the classloader for each class.
These kind of problems are not uncommon in appserver-style environments where application code and infratructure code are deliberately isolated - for example if system classes are accidentally included in applciation JARs you can have two copies of the "same" class inthe JVM and life gets confusing
James McMahon : "You should be able to dump not only the type hierarchy but also the identity of the classloader for each class." How do you implement this?James McMahon : This is a good suggestion, however after running .getClassLoader() on them both classes appear to be loaded by the same class loader.James McMahon : Just for other folks who may be troubleshooting, there is more about the class loader issue at http://stackoverflow.com/questions/826319/classcastexception-when-casting-to-the-same-class.
0 comments:
Post a Comment