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
}
Из-за операторов возврата я не могу понять, почему любой из них должен быть более эффективным или быстрым, чем другой. Что касается определенного объекта, оба метода должны были бы сделать равное количество проверок, насколько я могу видеть. И из-за возвратных операторов никакой дополнительный код не будет запущен ни в одном из них.
Я что-то упустил? Есть что-то? Есть ли какие-то оптимизации компилятора или что-то происходит или что-то еще?
Я знаю, что это микро-оптимизация, и я, скорее всего, буду придерживаться первого в любом случае, так как я думаю, что он выглядит более чистым со всеми ifs на той же позиции. Но я не могу с этим поделать; Мне любопытно!
Ответы
Ответ 1
Сгенерированный байт-код идентичен для этих двух случаев, поэтому это чисто вопрос стиля.
Я создал два метода 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
Я оставил код, который я поставил после этого, чтобы скомпилировать его.
Ответ 2
Ни один из них не более эффективен, чем другой. Компилятор может легко увидеть, что эти два идентичны, и на самом деле Suns/Oracles 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
означает, что программист не понимал, что это не нужно.
Ответ 3
Я не вижу практической причины заменить одну из этих реализаций другой - в любом направлении.
Второй пример имеет смысл, если вы хотите избежать нескольких операторов возврата одним способом - некоторые люди предпочитают этот способ кодирования. Тогда нам нужны конструкции 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;
}
Ответ 4
Подумайте об этом так. Когда выполняется оператор return, элемент управления оставляет этот метод, поэтому else
действительно не добавляет никакого значения, если вы не хотите утверждать, что он добавляет читаемость (что я действительно не думаю, что это так, но другие могут не согласиться).
Итак, когда у вас есть:
if (someCondition)
return 42;
if (anotherCondition)
return 43;
Нет никакого значения при добавлении else
ко второй if
.
Фактически, я использую инструмент при написании кода С# под названием Resharper, и он фактически помечает else
как бесполезный код в этих ситуациях. Поэтому я думаю, что в целом лучше оставить их. И, как уже упоминал Йоахим, компилятор в любом случае оптимизирует их.
Ответ 5
Я думаю, что этот код можно немного улучшить (заметьте, он очень читабель):
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Оператор instanceof
эквивалентен обеим комбинированным и, вероятно, быстрее - меньше кода и не вызывает вызовы метода:
if (!(obj instanceof MyClass))
return false;
Но что я знаю... Я слишком ленив, чтобы анализировать байтовый код (никогда не делал этого раньше).:-p