Подтвердить что ты не робот

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 на той же позиции. Но я не могу с этим поделать; Мне любопытно!

4b9b3361

Ответ 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