Java: if-return-if-return vs if-return-elseif-return
попросил недетский вопрос где у меня был такой код:
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
// Check property values
}
Я получил комментарий, который утверждал, что это не оптимально, и что вместо этого (если я правильно понял) должен сделать это:
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
}
из-за операторов возврата я не могу понять, почему любой из них должен быть более эффективным или быстрее, чем другой. Учитывая определенный объект, оба метода должны были бы выполнить равное количество проверок, насколько я могу видеть. И из-за инструкции return, никакой дополнительный код не будет работать ни в одном из них.
Я что-то пропустил? В этом что-то есть? Есть ли какие-то оптимизации компилятора или что-то происходит или что-то еще?
Я знаю, что это микро-оптимизация, и я, скорее всего, буду придерживаться первого в любом случае, так как я думаю, что он выглядит чище со всеми ifs на той же позиции. Но я ничего не могу с собой поделать, мне любопытно!
5 ответов
сгенерированный байтовый код идентичен для этих двух случаев, поэтому это чисто вопрос стиля.
Я произвел два метода e1
и e2
и оба произвели этот байтовый код (Читайте с помощью 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
Я опустил код, который я поставил после этого, чтобы сделать его компиляции.
ни один из них не является более эффективным, чем другой. Компилятор может легко увидеть, что оба они идентичны, и на самом деле Солнца/оракулы javac
производит идентичный байт-кода для двух методов.
здесь IfTest
класс:
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;
}
}
я скомпилировал его с javac
и разборка выглядит следующим образом:
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
то есть, я бы рекомендовал использовать первый вариант (без else
). Некоторые люди могут утверждают, что это чище С остальные части, но я бы сказал обратное. в том числе на else
указывает на то, что программист не понял, что в этом нет необходимости.
Я не вижу никакой практической причины заменять одну из этих реализаций другой-в любом направлении.
второй пример имеет смысл, если вы хотите избежать нескольких операторов return в одном методе-некоторые люди предпочитают этот способ кодирования. затем нам нужны конструкции if-else if:
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;
}
подумайте об этом таким образом. Когда выполняется оператор return, элемент управления покидает метод, поэтому else
на самом деле не добавляет никакого значения, если вы не хотите утверждать, что он добавляет читаемость (что я действительно не думаю, но другие могут не согласиться).
Итак, когда у вас есть:
if (someCondition)
return 42;
if (anotherCondition)
return 43;
на самом деле нет никакого значения в добавлении else
второй if
.
на самом деле, я использую инструмент при написании кода C# под названием Resharper, и это на самом деле будет отмечать else
как бесполезный код в этих ситуациях. Поэтому я думаю, что в целом, лучше оставить их. И, как уже упоминал Иоахим, компилятор оптимизирует их в любом случае.
Я думаю, что этот код можно немного улучшить (имейте в виду, это очень читаемо):
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
на instanceof
оператор эквивалентен обоим из них и, вероятно, быстрее-меньше кода, и никаких вызовов метода:
if (!(obj instanceof MyClass))
return false;
но что я знаю.... Я слишком ленив, чтобы анализировать байтовый код (никогда не делал этого раньше). :- p