Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does the Java cast operator work?

Tags:

java

casting

jvm

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?

like image 794
James McMahon Avatar asked May 08 '09 15:05

James McMahon


2 Answers

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 final class (§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 final class (§8.1.1), then S must implement T, or a compile-time error occurs.

    • 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 (because Object is 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.Serializable or the type Cloneable, the only interfaces implemented by arrays.
    • If T is a type variable, then:
      • If the upper bound of T is Object or the type java.io.Serializable or the type Cloneable, 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 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.

like image 186
Michael Myers Avatar answered Oct 19 '22 23:10

Michael Myers


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

like image 38
djna Avatar answered Oct 20 '22 00:10

djna