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