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
精彩评论