Often, writing code requires to test assumptions. The easiest way is often to do some research on the net. But when the first few queries do not return a useful result, it is time to test the assumption by ourselves.

In my case, I was wondering whether explicitly casting a variable to its static type introduces a cast in the compiled bytecode1. For instance, in the program below the cast from int to int is useless and could be removed during compilation.

int a = 1;
System.out.println(((int) a) + 1);

Of course, if written by hand, the best design decision is to simply remove the cast and to continue coding. But, in my case, the Java code is generated by a compiler, which left me two choices. Either add some logic to my compiler and remove useless casts during code generation. Or, left them in place and let the Java compiler do the heavy lifting.

I’d rather avoid complexifying my compiler for now, and I’d be happier with a few useless casts in the generated Java source code, but only if they are finally removed from the Java bytecode.

This is why I want to validate the following question:

Does javac cleanup useless type cast?

I based my experiment on the sample below, where a callMe(B b) method is called three times on different configurations. The first call is our baseline, what one would write manually. The second call introduce a useless cast since b is already statically of type B. Finally, the third does a down-cast of a to type B.

Only the later is expected to produce a cast operation in the compiled bytecode.

public class Demo {
    static class A {}
    static class B extends A {}

    private static void callMe(B b) {}

    public static void main(String[] args) {
        B b = new B();
        A a = new B();
        callMe(b);         // nominal method invocation
        callMe((B) b);     // useless cast
        callMe((B) a);    // useful cast

Compiling the java classes is straightforward and lead to the compilation of three classes: WithCasts and the two inner classes WithCasts$A and WithCasts$B.

$ mkdir -p bin
$ javac -d bin WithCasts.java
$ tree
├── bin
│   ├── WithCasts$A.class
│   ├── WithCasts$B.class
│   └── WithCasts.class
└── WithCasts.java

Now, time to inspect the bytecode using javap2.

I won’t go into the details of how to read bytecode, but I found it useful to get enough knowledge to be minimally knowledgeable on the topic3. The bytecode below is annotated in order to ease its reading.

$ javap -cp bin -c WithCasts   
Compiled from "WithCasts.java"
public class WithCasts {
  public WithCasts();
       0: aload_0
       1: invokespecial #1    // Method java/lang/Object."<init>":()V
       4: return

  public static void callMe(WithCasts$B);
       0: return

  public static void main(java.lang.String[]);
# B b = new B();
       0: new           #2    // class WithCasts$B
       3: dup
       4: invokespecial #3    // Method WithCasts$B."<init>":()V
       7: astore_1
# A a = new B();
       8: new           #2    // class WithCasts$B
      11: dup
      12: invokespecial #3    // Method WithCasts$B."<init>":()V
      15: astore_2
# callMe(b);
      16: aload_1
      17: invokestatic  #4    // Method callMe:(LWithCasts$B;)V
# callMe((B) b);
      20: aload_1
      21: invokestatic  #4    // Method callMe:(LWithCasts$B;)V
# callMe((B) a);
      24: aload_2
      25: checkcast     #2    // class WithCasts$B
      28: invokestatic  #4    // Method callMe:(LWithCasts$B;)V
# implicit void return
      31: return

We now have enough information to validate our hypothesis, callMe((B) b); line 20-21 does not call the checkcast operation4. Hence, we can conclude that javac remove useless cast during compilation.

Consequently, I can prevent the introduction of additional complexity in my compiler while keeping a clean and efficient bytecode.

  1. But I will not discuss whether using explicit type casts is a good practice in this post. 

  2. javap documentation: https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javap.html

  3. An introduction to bytecode: https://dzone.com/articles/introduction-to-java-bytecode

  4. checkcast documentation: https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.checkcast