A co-worker told me Class.cast() is better than cast opertor as it’s safer to handle null reference. Really?
Let’s take a look at the following code snippet. As you can see, basically, there is no difference. Both test cases can pass.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
@Test public void testCastOpertor() { Object obj = null; Integer a = (Integer)obj; assertNull(a); obj = new Integer(10); a = (Integer)obj; assertEquals(new Integer(10), a); obj = new Double(20.0); try { a = (Integer) obj; assertTrue(false); } catch (ClassCastException e) { assertTrue(true); } } @Test public void testCastFunction() { Object obj = null; Integer a = Integer.class.cast(obj); assertNull(a); obj = new Integer(10); a = Integer.class.cast(obj); assertEquals(new Integer(10), a); obj = new Double(20.0); try { a = Integer.class.cast(obj); assertTrue(false); } catch (ClassCastException e) { assertTrue(true); } } |
In the function of cast(), it does null and type check but still uses cast operator to cast object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/** * Casts an object to the class or interface represented * by this {@code Class} object. * * @param obj the object to be cast * @return the object after casting, or null if obj is null * * @throws ClassCastException if the object is not * null and is not assignable to the type T. * * @since 1.5 */ public T cast(Object obj) { if (obj != null && !isInstance(obj)) throw new ClassCastException(cannotCastMsg(obj)); return (T) obj; } |
What’s the difference in byte code? See the source code and byte code (compiled by Java 7) of a simple test case.
1 2 3 4 5 6 |
@Test public void testComparison() { Object obj = new Integer(10); Integer b = (Integer)obj; Integer c = Integer.class.cast(obj); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// access flags 0x1 public testComparison()V @Lorg/junit/Test;() L0 LINENUMBER 16 L0 NEW java/lang/Integer DUP BIPUSH 10 INVOKESPECIAL java/lang/Integer.<init> (I)V ASTORE 1 L1 LINENUMBER 17 L1 ALOAD 1 CHECKCAST java/lang/Integer ASTORE 2 L2 LINENUMBER 18 L2 LDC Ljava/lang/Integer;.class ALOAD 1 INVOKEVIRTUAL java/lang/Class.cast (Ljava/lang/Object;)Ljava/lang/Object; CHECKCAST java/lang/Integer ASTORE 3 L3 LINENUMBER 19 L3 RETURN L4 LOCALVARIABLE this Lorg/yli/algo/TreeTest; L0 L4 0 LOCALVARIABLE obj Ljava/lang/Object; L1 L4 1 LOCALVARIABLE b Ljava/lang/Integer; L2 L4 2 LOCALVARIABLE c Ljava/lang/Integer; L3 L4 3 MAXSTACK = 3 MAXLOCALS = 4 |
Note: Internally, line index starts from 0.
For L1: Instruction of “CHECKCAST” will be invoked to do type casting. In JVM specification, you can find all details about it. Basically, it behaves the same as we expects. See more details here.
For L2: Function cast() will be invoke first and later “CHECKCAST” still needed. Why? I think Java compiler plays a trick. cast() is a generic function, which should return an object in T type. But in runtime, information of type will be erased. So compiler need to put an extra instruction here to do type casting, which should be the same for other generic functions. Actually, in cast(), “CHECKCAST” is not really invoked.
In that case, way to use case() has worse performance.
See a simple performance test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@Test public void testPerformance() { Object obj = new Integer(10); long startTime = System.nanoTime(); for (int i = 0; i < 100000; ++i) { Integer b = (Integer) obj; } System.out.println( String.format("Elapsed time for cast operator: %fms", (System.nanoTime() - startTime) / 1000000.0)); startTime = System.nanoTime(); for (int i = 0; i < 100000; ++i) { Integer c = Integer.class.cast(obj); } System.out.println( String.format("Elapsed time for cast(): %fms", (System.nanoTime() - startTime) / 1000000.0)); } |
And the difference is BIG.
1 2 |
Elapsed time for cast operator: 1.639244ms Elapsed time for cast(): 6.964643ms |
Conclusion
I think in most cases, cast operator is safe and simple. But in some cases, when you only have Class object, such as some generic function/class, cast() is also a good choice.