web-dev-qa-db-ja.com

Java:if-return-if-returnと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ステートメントのため、なぜそれらのどれが他よりも効率的または高速である必要があるのか​​、実際にはわかりません。特定のオブジェクトが与えられた場合、両方のメソッドは、私の知る限り、同じ数のチェックを実行する必要があります。また、returnステートメントがあるため、追加のコードが実行されることはありません。

ここで何か不足していますか?何かありますか?コンパイラの最適化や何かが起こっているのでしょうか?

これはマイクロ最適化であることを知っており、同じ位置にあるすべてのifでクリーンに見えるので、どちらかの方法で最初に固執する可能性があります。しかし、私はそれを助けることはできません。気になる!

27
Svish

生成されたバイトコードはこれら2つのケースで同一であるため、純粋にスタイルの問題です。

2つのメソッドを作成しましたe1およびe2と両方がこのバイトコードを生成しました(javap -v):

 public boolean e1(Java.lang.Object); 
コード:
 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; //メソッドJava/lang/Object.getClass :()Ljava/lang/Class; 
 17:aload_1 
 18:invokevirtual#25; //メソッドJava/lang/Object.getClass :()Ljava/lang/Class; 
 21:if_acmpeq 26 
 24:iconst_0 
 25:ireturn 

コンパイルするために、その後に置いたコードを省略しました。

23
Joachim Sauer

どちらも他より効率的ではありません。コンパイラは2つが同一であることを簡単に確認できます。実際、Suns/Oracles javacは2つのメソッドに対して同一のバイトコードを生成します。

これが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の部分がよりクリーンwithであると主張するかもしれませんが、私は反対を主張します。 含むelseは、プログラマがそれが不要であることを認識していなかったことを示します。

9
aioobe

これらの実装の1つを他の実装に置き換える実際的な理由はありません。

2番目の例は、1つのメソッドで複数のreturnステートメントを回避したい場合に意味があります。そのようなコーディングを好む人もいます。 Then 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;
}
5
Andreas_D

このように考えてください。 returnステートメントが実行されると、コントロールがメソッドを離れるので、読みやすさを追加すると主張したくない限り、elseは実際には値を追加しません(実際にはそうは思わないが、他の同意しない場合があります)。

だからあなたが持っているとき:

if (someCondition)
    return 42;

if (anotherCondition)
    return 43;

2番目のelseifを追加しても、実際には値はありません。

実際、私は Resharper と呼ばれるC#コードを記述するときにツールを使用します。このような状況では、実際にelseは役に立たないコードとしてマークされます。したがって、一般的には、それらを省略した方がよいと思います。ヨアヒムがすでに述べたように、コンパイラーはとにかくそれらを最適化します。

3
dcp

私はこのコードを少し改善できると思います(気を付けてくださいvery可読です):

  if (obj == null)
        return false;

  if (getClass() != obj.getClass())
        return false;

instanceof演算子は、これらの両方を組み合わせたものと同等であり、おそらくより高速です-コードが少なく、メソッドの呼び出しはありません。

  if (!(obj instanceof MyClass))
        return false;

しかし、私は何を知っていますか...バイトコードを分析するのが面倒です(これまでに行ったことがない)。 :-p

0
Julius Musseau