开发者

Java: if-return-if-return vs if-return-elseif-return

开发者 https://www.devze.com 2023-02-26 03:31 出处:网络
Asked an unrelated question where I had code like this: public boolean equals(Object obj) { if (this == obj)

Asked an unrelated question where I had code like this:

public boolean equals(Object obj)
{
    if (this == obj)
        return true;

    if (obj == null)
        return false;

    if (getClass() != obj.getClass())
        return false;

    // Check property values
}

I got a comment which claimed that this was not optimal, and that it instead (if I understood correctly) should do this:

public boolean equals(Object obj)
{
    if (this == obj)
        return true;

    else if (obj == null)
        return false;

    else if (getClass() != obj.getClass())
        return false;

    // Check property values
}

Because of the return statements, I can't really see why any of them should be mor开发者_如何转开发e efficient or faster than the other. Given a certain object, both methods would have to do an equal number of checks as far as I can see. And because of the return statements, no extra code would run in any of them.

Am I missing something here? Is there something to it? Are there some compiler optimizations or something going on or whatever?

I know this is micro optimization and I will most likely stick with the first either way, since I think it looks cleaner with all the ifs on the same position. But I can't help it; I'm curious!


The generated byte code is identical for those two cases, so it's purely a matter of style.

I produced two methods e1 and e2 and both produced this byte code (read using javap -v):

public boolean e1(java.lang.Object);
  Code:
   Stack=2, Locals=2, Args_size=2
   0:   aload_0
   1:   aload_1
   2:   if_acmpne   7
   5:   iconst_1
   6:   ireturn
   7:   aload_1
   8:   ifnonnull   13
   11:  iconst_0
   12:  ireturn
   13:  aload_0
   14:  invokevirtual   #25; //Method java/lang/Object.getClass:()Ljava/lang/Class;
   17:  aload_1
   18:  invokevirtual   #25; //Method java/lang/Object.getClass:()Ljava/lang/Class;
   21:  if_acmpeq   26
   24:  iconst_0
   25:  ireturn

I left out the code I put after that to make it compile.


Neither one is more efficient than the other. The compiler can easily see that the two are identical, and in fact Suns/Oracles javac produces identical bytecode for the two methods.

Here is an IfTest class:

class IfTest {

    public boolean eq1(Object obj) {
        if (this == obj)
            return true;

        if (obj == null)
            return false;

        if (getClass() != obj.getClass())
            return false;

        return true;
    }


    public boolean eq2(Object obj) {

        if (this == obj)
            return true;

        else if (obj == null)
            return false;

        else if (getClass() != obj.getClass())
            return false;

        return true;
    }
}

I compiled it with javac and the disassembly is as follows:

public boolean eq1(java.lang.Object);
  Code:
   0:   aload_0
   1:   aload_1
   2:   if_acmpne   7
   5:   iconst_1
   6:   ireturn
   7:   aload_1
   8:   ifnonnull   13
   11:  iconst_0
   12:  ireturn
   13:  aload_0
   14:  invokevirtual   #2; //Method Object.getClass:()Ljava/lang/Class;
   17:  aload_1
   18:  invokevirtual   #2; //Method Object.getClass:()Ljava/lang/Class;
   21:  if_acmpeq   26
   24:  iconst_0
   25:  ireturn
   26:  iconst_1
   27:  ireturn

 

public boolean eq2(java.lang.Object);
  Code:
   0:   aload_0
   1:   aload_1
   2:   if_acmpne   7
   5:   iconst_1
   6:   ireturn
   7:   aload_1
   8:   ifnonnull   13
   11:  iconst_0
   12:  ireturn
   13:  aload_0
   14:  invokevirtual   #2; //Method Object.getClass:()Ljava/lang/Class;
   17:  aload_1
   18:  invokevirtual   #2; //Method Object.getClass:()Ljava/lang/Class;
   21:  if_acmpeq   26
   24:  iconst_0
   25:  ireturn
   26:  iconst_1
   27:  ireturn

That is, I would recommend using the first version (without the else). Some people may argue that it's cleaner with the else parts, but I would argue the opposite. Including the else indicates that the programmer didn't realize that it was unnecessary.


I don't see any practical reason to replace one of those implementations with the other one - in any direction.

The second example would make sense if you wanted to avoid multiple return statements in one method - some people prefer that way of coding. Then we need the if-else if constructs:

public boolean equals(Object obj)
{
    boolean result = true;

    if (this == obj)
        result = true;

    else if (obj == null)
        result = false;

    else if (getClass() != obj.getClass())
        result = false;

    return result;
}


Think of it this way. When a return statement is executed, control leaves the method, so the else doesn't really add any value, unless you want to argue that it adds readability (which I don't really think it does, but others may disagree).

So when you have:

if (someCondition)
    return 42;

if (anotherCondition)
    return 43;

There's not really any value in adding an else to the second if.

In fact, I use a tool when writing C# code called Resharper, and it will actually mark the else as useless code in these situations. So I think that generally, it's better to leave them out. And as Joachim already mentioned, the compiler optimizes them away anyway.


I think this code can be improved a little (mind you, it is very readable):

  if (obj == null)
        return false;

  if (getClass() != obj.getClass())
        return false;

The instanceof operator is equivalent to both of those combined and is probably faster - less code, and no method invocations:

  if (!(obj instanceof MyClass))
        return false;

But what do I know.... I'm too lazy to analyze the byte code (having never done it before). :-p

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号