9

To my understanding, if type checking can be done during compilation, the type casting will be done during compilation and will not incur any runtime overhead.

For example

public Child getChild() {
  Parent o = new Child();
  return (Child) o;
}

Is the type casting done during compilation or during runtime?

And is there any general rule to decide if a type casting is done by javac compiler or by the VM?

qinsoon
  • 1,433
  • 2
  • 15
  • 34
  • Good question... but does this matter to code? Or are you just asking for your own curiosity? – James Cronen Jul 24 '12 at 13:35
  • 3
    @Tenner does it matter either way? – Jon Taylor Jul 24 '12 at 13:37
  • In this example, I expect type casting to be done during Compile time, and not during run time. However, you shouldn't need to cast o to a Child for the return statement, as Java knows it's of type Child based on the Object graph. – Jay Jul 24 '12 at 13:39
  • 1
    @Jay You are suggesting OP to introduce a compiler error. – Marko Topolnik Jul 24 '12 at 13:41
  • @Marko, not if Parent extends Child, which I assumed, which is seldom a good idea, my apologies. – Jay Jul 24 '12 at 13:44
  • @Jay If Parent extended child then `Parent o = new Child(); ` would not wrok, either way what you said is wrong. – Jon Taylor Jul 24 '12 at 13:45
  • @Tenner Usually this doesn't matter much. I am curious if there is any kind of coding pattern or say, syntax restriction, could make the javac compiler to remove the type casting instead of delaying it to the runtime (because it is clear that this kind of type casting can be inferred statically) – qinsoon Jul 25 '12 at 00:36
  • Am an beginner, could you please be more clear, am not able to understand your complex terms, can i get answer in at most 4 lines in simple terms about question? – Vinay Apr 06 '17 at 14:48
  • is it something you have explained here?? http://stackoverflow.com/a/13406677/3164176 – Vinay Apr 06 '17 at 15:07

5 Answers5

2

Actually, there are three possibilities in this case:

  1. The javac compiler could perform the optimization.
  2. The JIT compiler could perform the optimization.
  3. The native code by the JIT compiler could include code to do a runtime type check.

I expect that it is option 1. or 2. but this could be platform specific.


In fact, on my system the bytecode is not optimized. If any optimization is to occur it will be up the the JIT compiler to do it. (This fits with what I've heard ... that most Java bytecode compilers do little in the way of optimization before generating bytecodes.)

Compiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
  Code:
   0:   aload_0
   1:   invokespecial   #8; //Method java/lang/Object."<init>":()V
   4:   return

public Child getChild();
  Code:
   0:   new #16; //class Child
   3:   dup
   4:   invokespecial   #18; //Method Child."<init>":()V
   7:   astore_1
   8:   aload_1
   9:   checkcast   #16; //class Child
   12:  areturn

}
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • @StephenC I see the same results. If I set `getChild()` as a private method, it seems that the optimization occurs. I have no more `getChild()` block in the javap -c log (see my answer). I don't really understand why it makes a difference :/ – alain.janinm Jul 24 '12 at 17:17
  • @StephenC Thank you. I totally agree with the 3 possibilities you listed. My expectation is javac or other bytecode compiler would remove some type casting, since there is little point to do it lazily during execution. And further I am wondering if any coding pattern could trigger the optimization in bytecode compiler. But it seems javac won't do this kind of optimization. – qinsoon Jul 25 '12 at 00:43
  • *"But it seems javac won't do this kind of optimization."* - The bottom line is that it doesn't really matter where the optimization gets done, provided that is *does* get done. – Stephen C Jul 25 '12 at 05:05
1

When a running program attempts to cast an object reference to another type, the virtual machine must check to see if the type being cast to is the actual class of the referenced object or one of its supertypes. It must perform the same kind of check when a program performs an instanceof operation.

In either case, the virtual machine must look into the class data of the referenced object. When a program invokes an instance method, the virtual machine must perform dynamic binding: it must choose the method to invoke based not on the type of the reference but on the class of the object. To do this, it must once again have access to the class data given only a reference to the object.

Edit:

Java compiler is not responsible for checking if the casting is correct or not, just like some of the bindings only occur at run time. Java virtual machine does the checking at run time to find out whether the actual reference object is a legitimate object of the new type. If not, there will be a runtime exception: ClassCastException.

developer
  • 9,116
  • 29
  • 91
  • 150
  • 1
    `javac` could have compiled that code with no downcasts, so the runtime wouldn't ever know there was a `Parent` type involved. – Marko Topolnik Jul 24 '12 at 13:43
0

I'd guess it's done in both stages. At compile time, the compiler will force you to do the right casts to ensure you didn't mix up the types, as in any strongly typed language.

If you however cast an Object you got, say, as a parameter into String (which will work for Objects that are actually instanceof String) the JVM will still have to make sure that the implementing class of Object truly extends or is String and you will get a ClassCastException if it isn't.

Miquel
  • 15,405
  • 8
  • 54
  • 87
0

For the conversions that don't require a test at run-time, it may be possible that the compiler make some optimisations to avoid casting at run-time.

I suggest to read the JLS Chapter 5. Conversions and Promotions to know more about the type of conversions that need a test at run-time.

Example 5.0-1. Conversions at Compile-time and Run-time

A conversion from type Object to type Thread requires a run-time check to make sure that the run-time value is actually an instance of class Thread or one of its subclasses; if it is not, an exception is thrown.

A conversion from type Thread to type Object requires no run-time action; Thread is a subclass of Object, so any reference produced by an expression of type Thread is a valid reference value of type Object.

A conversion from type int to type long requires run-time sign-extension of a 32-bit integer value to the 64-bit long representation. No information is lost.

A conversion from type double to type long requires a nontrivial translation from a 64-bit floating-point value to the 64-bit integer representation. Depending on the actual run-time value, information may be lost.

5.1.6. Narrowing Reference Conversion :

Such conversions require a test at run-time to find out whether the actual reference value is a legitimate value of the new type. If not, then a ClassCastException is thrown.

5.1.8. Unboxing Conversion ; The conversion proceed at run-time.

Also see : 5.5.3. Checked Casts at Run-time

It's not so easy to determine when the conversion happened for example :

public class Main {

    private static class Child extends Parent{

        public Child() {
        }
    }

    private static class Parent {

        public Parent() {
        }
    }

    private static Child getChild() {
        Parent o = new Child();
        return (Child) o;
    }


    public static void main(final String[] args) {
        Child c = getChild();
    }
}

The result given by javap -c Main is :

public class Main extends java.lang.Object{
public Main();
Code:
0:  aload_0
1:  invokespecial   #1; //Method java/lang/Object."<init>":()V
4:  return

public static void main(java.lang.String[]);
Code:
0:  invokestatic    #4; //Method getChild:()LMain$Child;
3:  astore_1
4:  return

}

If you change the method declaration to public static Child getChild() the result is :

public class Main extends java.lang.Object{
public Main();
Code:
0:  aload_0
1:  invokespecial   #1; //Method java/lang/Object."<init>":()V
4:  return

public static Main$Child getChild();
Code:
0:  new #2; //class Main$Child
3:  dup
4:  invokespecial   #3; //Method Main$Child."<init>":()V
7:  astore_0
8:  aload_0
9:  checkcast   #2; //class Main$Child
12: areturn

public static void main(java.lang.String[]);
Code:
0:  invokestatic    #4; //Method getChild:()LMain$Child;
3:  astore_1
4:  return

}

You see that just changing the accessor, can impact a lot on the possible optimisations.

alain.janinm
  • 19,951
  • 10
  • 65
  • 112
  • I checked. The 'private static getChild()' still exists in the class. Just for some reason (or bug) that javap doesn't output it. And the bytecode is no difference either. – qinsoon Jul 25 '12 at 00:48
0

When I compiled

public class Test {
    public Child getChildVersion1() {
        Parent o = new Child();
        return (Child) o;
    }
    public Child getChildVersion2() {
        return new Child();
    }
}

and decompiled that code using javap -c Test at Java 7 (Windows 7 64bit) it gave me this result

Compiled from "Test.java"
public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public Child getChildVersion1();
    Code:
       0: new           #2                  // class Child
       3: dup
       4: invokespecial #3                  // Method Child."<init>":()V
       7: astore_1
       8: aload_1
       9: checkcast     #2                  // class Child
      12: areturn

  public Child getChildVersion2();
    Code:
       0: new           #2                  // class Child
       3: dup
       4: invokespecial #3                  // Method Child."<init>":()V
       7: areturn
}

So it seams that compiler didn't optimize method getChildVersion1 to be like getChildVersion2 so beside checking type while compilation time there is also checking at runtime ( 9: checkcast #2 ). But as Stephen C mentioned it may be related to platform (OS, Java version).

Community
  • 1
  • 1
Pshemo
  • 122,468
  • 25
  • 185
  • 269