Search code examples
javaequalsdowncast

Same multiple downcasts in equals; will they be optimised?


I tend to write my equals method in Java as a one liner...

class Test {
   private String a = "";
   private Integer b = Integer.MIN_VALUE;
   private Long c = Long.MIN_VALUE;

   public Test(final String a, final Integer b, final Long c) {
       this.a = a;
       this.b = b;
       this.c = c;
   }

   @Override
   public boolean equals(final Object obj) {
        return obj instanceof Test && ((Test) obj).a.equals(this.a)
          && ((Test) obj).b.equals(this.b)
          && ((Test) obj).c.equals(this.c);
   }
}

As you can see, in this approach I downcast the Object instance to Test instance many times. My question is, will it be optimised by the compiler so there will be a single downcast instead of three like if I wrote my equals method like this?

 public boolean equals(final Object obj) {
     if (obj instanceof Test) {
         final Test test = (Test) obj;
         return test.a.equals(this.a) && test.b.equals(this.b)
           && test.c.equals(this.c);
     } else {
         return false;
     }
 }

It is not a duplicate of the question in comment, because I am interested here in brevity of the implementation of the equals method (6 lines against 3) but not at cost of a performance degradation. In the other question the difference is one line.


Solution

  • When I decompile the class I find that the Eclipse compiler and javac indeed produce three checkcast instructions in the equals method, so no optimization takes place.

    When optimized by Hotspot it could be that hotspot can figure out that one cast is enough.

    The decompiled byte code:

    public boolean equals(java.lang.Object);
       Code:
          0: aload_1
          1: instanceof    #13                 // class sov/Test
          4: ifeq          62
          7: aload_1
          8: checkcast     #13                 // class sov/Test
         11: getfield      #3                  // Field a:Ljava/lang/String;
         14: aload_0
         15: getfield      #3                  // Field a:Ljava/lang/String;
         18: invokevirtual #14                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
         21: ifeq          62
         24: aload_1
         25: checkcast     #13                 // class sov/Test
         28: getfield      #7                  // Field b:Ljava/lang/Integer;
         31: aload_0
         32: getfield      #7                  // Field b:Ljava/lang/Integer;
         35: invokevirtual #15                 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
         38: ifeq          62
         41: aload_1
         42: checkcast     #13                 // class sov/Test
         45: getfield      #12                 // Field c:Ljava/lang/Long;
         48: aload_0
         49: getfield      #12                 // Field c:Ljava/lang/Long;
         52: invokevirtual #16                 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z
         55: ifeq          62
         58: iconst_1
         59: goto          63
         62: iconst_0
         63: ireturn