Java Static vs Dynamic Casting

Every time I needed to cast objects in Java I always thought dynamic casting, using Class.cast() was more expensive than simply using the (cast) operator. But apparently in Java, static vs dynamic casting don’t mean much difference.

Let’s consider this code.

package net.superglobals;

public interface Action {
	void perform();
}

And a class that implements my interface.

package net.superglobals;

public class SendEmail implements Action {

	@Override
	public void perform() {
		System.out.println("Canadian Pharmacy Order the Cheapest Medication Now!!");
	}

}

Now I’ll create a situation that requires casting. I’m simply using the cast operator.

package net.superglobals;

public class Main {
	public static void main(String[] args ) {
		Object o = new SendEmail();
		SendEmail sameThing = (SendEmail) o;
		sameThing.perform();
	}
}

If we were to disassemble the generated class and look at the bytecode we find this. It basically:

  1. new creates an object and does some boilerplate storing it and so on
  2. Read more about dup, astore_n, aload_n. 1 is basically our o variable, and 2 is the sameThing variable.
  3. invokespecial invokes the <init> method of the SendEmail class
  4. checkcast checks if the top variable on stack (aka 1, which is o) can be cast to net.superglobals.SendEmail.
  5. stores it in variable 2 then finally invokes the class method perform().
.method public static main : ([Ljava/lang/String;)V 
    .code stack 2 locals 3 
L0:     new net/superglobals/SendEmail 
L3:     dup 
L4:     invokespecial Method net/superglobals/SendEmail <init> ()V 
L7:     astore_1 
L8:     aload_1 
L9:     checkcast net/superglobals/SendEmail 
L12:    astore_2 
L13:    aload_2 
L14:    invokevirtual Method net/superglobals/SendEmail perform ()V 
L17:    return 

All good and pretty self explanatory until now. Let’s continue in our Java static vs dynamic casting dilemma.

Let’s create a function that takes an arbitrary class and tries to cast the first argument to that type. I’ll call this ‘dynamic’ casting since it’s not known at compile time. (Well it is written in the source code but in that Class<T> param any class can be passed. We can program it to change on user input and so on… 🙂 ).

package net.superglobals;

public class Main {
	public static void main(String[] args ) {
		Object o = new SendEmail();
		SendEmail sameThing = dynamicCast(o, SendEmail.class);
		sameThing.perform();
	}
	
	public static <T> T dynamicCast(Object o, Class<T> clasz) {
		return clasz.cast(o);
	}
}

This above translates to the following bytecode. It’s pretty much as the one above, except it calls the static method dynamicCast before doing checkcast.

.method public static main : ([Ljava/lang/String;)V 
    .code stack 2 locals 3 
L0:     new net/superglobals/SendEmail 
L3:     dup 
L4:     invokespecial Method net/superglobals/SendEmail <init> ()V 
L7:     astore_1 
L8:     aload_1 
L9:     ldc Class net/superglobals/SendEmail 
L11:    invokestatic Method net/superglobals/Main dynamicCast (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object; 
L14:    checkcast net/superglobals/SendEmail 
L17:    astore_2 
L18:    aload_2 
L19:    invokevirtual Method net/superglobals/SendEmail perform ()V 
L22:    return 

We now move to the bytecode of our dynamicCast class. Basically local variables 1 and 0 are loaded onto the operand stack. Then invokevirtual is called with Method java/lang/Class cast which expects two operands on the stack. It pops them and…?

.method public static dynamicCast : (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object; 
    .signature '<T:Ljava/lang/Object;>(Ljava/lang/Object;Ljava/lang/Class<TT;>;)TT;' 
    .code stack 2 locals 2 
L0:     aload_1 
L1:     aload_0 
L2:     invokevirtual Method java/lang/Class cast (Ljava/lang/Object;)Ljava/lang/Object; 
L5:     areturn 
L6:     
        .linenumbertable 
            L0 11 
        .end linenumbertable 
        .localvariabletable 
            0 is o Ljava/lang/Object; from L0 to L6 
            1 is clasz Ljava/lang/Class; from L0 to L6 
        .end localvariabletable 
        .localvariabletypetable 
            1 is clasz Ljava/lang/Class<TT;>; from L0 to L6 
        .end localvariabletypetable 
    .end code 
.end method 

Well that’s a problem because that’s in the source of Java and it’s not shown in our decompiled file. But if we were to check the source of java.lang.Class we can search for the cast method and see:

@SuppressWarnings("unchecked")
public T cast(Object obj) {
    if (obj != null && !isInstance(obj))
        throw new ClassCastException(cannotCastMsg(obj));
    return (T) obj;
}

This method simply defines a generic type T and tries to cast it using the regular (cast) operator.

Basically, in Java static vs dynamic casting doesn’t mean much difference. Other than the fact Class.cast() has some overhead because it calls more methods, imho you can use both things without any worry.

FacebookTwitterLinkedin