CAST: difference between "(String) arg" and "String.class.cast (arg)"

7

I wonder if there is a difference between:

String a = (String) arg;

and cast class:

String a = String.class.cast(arg);

I once heard that using the static cast class is more performative, is this true?

    
asked by anonymous 06.07.2015 / 14:20

2 answers

6

Both execute the same task, but the so-called "static" version generates more bytecode (more overhead at compile time).

In practice both cast using the bytecode statement:

CHECKCAST java/lang/String

The difference that the code generated by the "static" version gets the type declaratively, in the String.class case, making a Class.cast (Object)

reference code:

public class CastTest2 {

    public static void main(String... args) {

        Object arg = "Test";

        String explicitCast = (String) arg;

        String explicitDynCast = String.class.cast(arg);

    }

bytecode generated for direct cast:

   L1
    LINENUMBER 10 L1
    ALOAD 1
    CHECKCAST java/lang/String
    ASTORE 2

bytecode generated for "static" cast:

   L2
    LINENUMBER 12 L2
    LDC Ljava/lang/String;.class
    ALOAD 1
    INVOKEVIRTUAL java/lang/Class.cast (Ljava/lang/Object;)Ljava/lang/Object;
    CHECKCAST java/lang/String
    ASTORE 3

Thus:

LDC Ljava/lang/String;.class

The above statement returns an instance of Class

which in turn calls the cast method, returning an object:

INVOKEVIRTUAL java/lang/Class.cast (Ljava/lang/Object;)Ljava/lang/Object;

and implicitly does Cast for Type, Class T.

CHECKCAST java/lang/String

ps: The fact that I call "static" and how it can be seen in the bytecode that calling the Class.cast (object) is done using the INVOKEVIRTUAL statement and not INVOKESTATIC.

    
06.07.2015 / 15:08
3

As said by Felipe in the comments, for comparative performance issues you just have to do a simple benchmarking of the two approaches.

In this example we used Metrics , version 3.1.0 . If you use , simply add this dependency:

<dependency>
    <groupId>io.dropwizard.metrics</groupId>
    <artifactId>metrics-core</artifactId>
    <version>3.1.0</version>
</dependency>

Other considerations:

  • JRE 1.8 update 45
  • Windows 8.1 Pro
  • Run in Eclipse Luna, release 2, version 4.4.2, Build id 20150219-0600

10000 casts of objects to String , both using cast operator , and using cast(Object obj) .

Below are the excerpts used for cast:

  • using cast operator
static void usingCastMethod() {
    final Object obj = "string";
    for (int i = 0; i < 10000; i++) {
        final String str = String.class.cast(obj);
    }
}

Result: 629.19 milliseconds

static void usingCastOperator() {
    final Object obj = "string";
    for (int i = 0; i < 10000; i++) {
        final String str = (String) obj;
    }
}

Result: 3065.02 milliseconds

Obs. : The result is only for comparison, just to see that there is a difference in performance.

From the result we can see that there is a significant difference, that is, we can conclude that this is not treated differently by the compiler.

Now, let's look at the bytecode generated in both approaches:

  • using cast operator
  static void usingCastMethod();
    Code:
       0: ldc           #104                // String string
       2: astore_0
       3: iconst_0
       4: istore_1
       5: goto          21
       8: ldc           #106                // class java/lang/String
      10: aload_0
      11: invokevirtual #113                // Method java/lang/Class.cast:(Ljava/lang/Object;)Ljava/lang/Object;
      14: checkcast     #106                // class java/lang/String
      17: astore_2
      18: iinc          1, 1
      21: iload_1
      22: sipush        10000
      25: if_icmplt     8
      28: return
  static void usingCastOperator();
    Code:
       0: ldc           #104                // String string
       2: astore_0
       3: iconst_0
       4: istore_1
       5: goto          16
       8: aload_0
       9: checkcast     #106                // class java/lang/String
      12: astore_2
      13: iinc          1, 1
      16: iload_1
      17: sipush        10000
      20: if_icmplt     8
      23: return

Bytecode we can see that in fact using cast operator is more costly, if you check each statement in specification you will see in detail why this.

In summary it's because java knows the type just at runtime, so it has to turn around to make sure it does not give a ClassCastException . Already in the other approach, even generating more bytecode , you already left explicit the type you expect, if it is not ( isInstance(Object obj) ) a ClassCastException will be released, without the JVM seeking more information to try the cast

Already knowing that there are differences in performance, are there advantages and disadvantages? I can now remember only when we use generics:

  • to avoid warning at compile time when we are using generics (not just String ), ie instead:
@SuppressWarnings("unchecked")
public <T> T cast(final Object o) {
    return (T) o;
}

Prefer to use this:

public <T> T cast(final Class<T> clazz, final Object o) {
    return clazz.cast(o);
}
    
06.07.2015 / 18:20