web-dev-qa-db-ja.com

構文の悪い習慣を満たすためだけにreturnステートメントを持っていますか?

次のコードを検討してください。

public Object getClone(Cloneable a) throws TotallyFooException {

    if (a == null) {
        throw new TotallyFooException();
    }
    else {
        try {
            return a.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
    //cant be reached, in for syntax
    return null;
}

return null;は例外がキャッチされる可能性があるため必要です。ただし、そのような場合はすでにnullであるかどうかを確認しているため(呼び出しているクラスがクローンをサポートしていることを前提としています)、tryステートメントが失敗することはありません。

構文を満たし、コンパイルエラーを回避するために最後に余分なreturnステートメントを入れるのは悪い習慣ですか(コメントは到達しません)、またはこのような何かをコーディングするより良い方法がありますか? returnステートメントは不要ですか?

81
yitzih

追加のreturnステートメントを使用しない、より明確な方法は次のとおりです。 CloneNotSupportedExceptionもキャッチしませんが、呼び出し元に渡します。

if (a != null) {
    try {
        return a.clone();
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
}
throw new TotallyFooException();

ほとんどの場合、最初に持っているものよりも簡単な構文で終わる順序をいじることは可能です。

134
Kayaman

間違いなく到達できます。 catch句のスタックトレースのみを出力していることに注意してください。

_a != null_とそこにwillが例外であるシナリオでは、_return null_willに到達します。このステートメントを削除して、throw new TotallyFooException();に置き換えることができます。

一般に*nullがメソッドのvalidである場合(つまり、ユーザーexpects itであり、それが何かを意味する場合) 「データが見つかりません」または例外が発生したことを示す信号として返すことは、notのアイデアです。それ以外の場合、nullを返さない理由は問題ありません。

_Scanner#ioException_ メソッドを例に取ります:

このScannerの基になるReadableによって最後にスローされたIOExceptionを返します。このメソッドreturnsnullそのような例外が存在しない場合

この場合、戻り値nullには明確な意味があり、メソッドを使用すると、メソッドが何かを実行しようとしたためではなく、そのような例外がなかったためにnullが得られたことを確認できますそして失敗した。

*意味があいまいであってもnullを返したい場合があることに注意してください。たとえば、 _HashMap#get_

戻り値null必ずしもマップにキーのマッピングが含まれていないことを示すわけではありません。マップがキーを明示的にマップすることも可能ですnullcontainsKey操作を使用して、これら2つのケースを区別できます。

この場合、nullは、valuenullが見つかって返されたこと、またはハッシュマップに要求されたキーが含まれていないことを示すことができます。

98
Maroun

構文を満たし、コンパイルエラーを回避するために、最後に余分なreturnステートメントを入れることは悪い習慣ですか(説明されていないコメントで)

return nullは、到達不能ブランチの終端にとって悪い習慣だと思います。 RuntimeExceptionAssertionError も許容される)をスローすることをお勧めします。不明な状態です。開発者が何かを見逃しているため、このようなものがほとんどです(上記のように)(オブジェクトはnullでなく、クローンできない可能性があります)。

私が作る可能性が高いので、コードが到達不能であることを非常に確信していない限り(たとえばSystem.exit()の後)、 InternalError を使用しない可能性が高いVMよりも間違い。

カスタム例外(TotallyFooExceptionなど)を使用するのは、その「到達不能な行」に到達することが、その例外をスローする他の場所と同じことを意味する場合だけです。

26

CloneNotSupportedExceptionをキャッチしたため、コードで処理できます。しかし、それをキャッチした後は、文字通り、関数の最後に達したときに何をすべきかわかりません。これは、それを処理できなかったことを意味します。したがって、この場合はコードのにおいであり、私の見解では、CloneNotSupportedExceptionをキャッチすべきではないということです。

14
djechlin

Objects.requireNonNull()を使用して、パラメーターaがnullでないかどうかを確認したいと思います。したがって、コードを読んだときに、パラメーターがnullであってはならないことは明らかです。

そして、チェックされた例外を避けるために、CloneNotSupportedExceptionRuntimeExceptionとして再スローします。

どちらの場合も、このようなことが起こらない、またはそうでない理由で、ニーステキストを追加できます。

public Object getClone(Object a) {

    Objects.requireNonNull(a);

    try {
        return a.clone();
    } catch (CloneNotSupportedException e) {
        throw new IllegalArgumentException(e);
    }

}
12
Kuchi

このような状況では、私は書くだろう

_public Object getClone(SomeInterface a) throws TotallyFooException {
    // Precondition: "a" should be null or should have a someMethod method that
    // does not throw a SomeException.
    if (a == null) {
        throw new TotallyFooException() ; }
    else {
        try {
            return a.someMethod(); }
        catch (SomeException e) {
            throw new IllegalArgumentException(e) ; } }
}
_

興味深いことに、「tryステートメントが失敗することはありません」と言いますが、実行しないと主張するステートメントe.printStackTrace();を書くのに苦労しました。どうして?

おそらくあなたの信念はしっかりと保持されていません。私の意見では、あなたの信念はあなたが書いたコードに基づいているのではなく、むしろあなたのクライアントが前提条件に違反しないという期待に基づいているため、それは良いことです。パブリックメソッドを防御的にプログラムする方が良い。

ところで、あなたのコードは私のためにコンパイルされません。 aの型がCloneableであっても、a.clone()を呼び出すことはできません。少なくともEclipseのコンパイラはそう言っています。式a.clone()はエラーを返します

メソッドclone()は、タイプCloneableに対して未定義です

あなたの特定の場合に私がすることは

_public Object getClone(PubliclyCloneable a) throws TotallyFooException {
    if (a == null) {
        throw new TotallyFooException(); }
    else {
        return a.clone(); }
}
_

PubliclyCloneableは次によって定義されます

_interface PubliclyCloneable {
    public Object clone() ;
}
_

または、パラメータタイプをCloneableにすることが絶対に必要な場合は、少なくとも以下をコンパイルします。

_public static Object getClone(Cloneable a) throws TotallyFooException {
//  Precondition: "a" should be null or point to an object that can be cloned without
// throwing any checked exception.
    if (a == null) {
        throw new TotallyFooException(); }
    else {
        try {
            return a.getClass().getMethod("clone").invoke(a) ; }
        catch( IllegalAccessException e ) {
            throw new AssertionError(null, e) ; }
        catch( InvocationTargetException e ) {
            Throwable t = e.getTargetException() ;
            if( t instanceof Error ) {
                // Unchecked exceptions are bubbled
                throw (Error) t ; }
            else if( t instanceof RuntimeException ) {
                // Unchecked exceptions are bubbled
                throw (RuntimeException) t ; }
            else {
                // Checked exceptions indicate a precondition violation.
                throw new IllegalArgumentException(t) ; } }
        catch( NoSuchMethodException e ) {
            throw new AssertionError(null, e) ; } }
}
_
7

上記の例は有効で非常にJavaです。ただし、その戻り値を処理する方法に関するOPの質問に対処する方法は次のとおりです。

public Object getClone(Cloneable a) throws CloneNotSupportedException {
    return a.clone();
}

aがnullであるかどうかを確認してもメリットはありません。 NPEになります。スタックトレースの印刷も役に立ちません。スタックトレースは、処理場所に関係なく同じです。

役に立たないヌルテストや役に立たない例外処理でコードを無駄にすることには利点がありません。ジャンクを削除することにより、返品の問題は議論の余地があります。

(OPには例外処理にバグが含まれていることに注意してください。これが復帰が必要な理由です。OPは私が提案する方法を間違えなかったでしょう。)

6
Tony Ennis

構文の悪い習慣を満たすためだけにreturnステートメントを持っていますか?

他の人が述べたように、あなたの場合、これは実際には適用されません。

しかし、質問に答えるために、Lintタイプのプログラムはそれを理解していません。私は2つの異なるものがスイッチステートメントでこれを争う​​のを見ました。

    switch (var)
   {
     case A:
       break;
     default:
       return;
       break;    // Unreachable code.  Coding standard violation?
   }

notがブレークすることはコーディング標準違反であると不満を言いました。もう1つは、到達不能なコードであるためhavingであると不満を述べました。

このことに気づいたのは、2人の異なるプログラマーが、その日に実行したコードアナライザーに応じて、ブレークを追加、削除、追加、削除してコードを再チェックし続けたためです。

このような状況に陥った場合は、その1つを選んで異常にコメントしてください。それが最良かつ最も重要なポイントです。

5
Jos

「構文を満たすだけ」ではありません。すべてのコードパスがリターンまたはスローにつながることは、言語のセマンティック要件です。このコードは準拠していません。例外がキャッチされた場合、次の返品が必要です。

それについて、またはコンパイラー全般を満足させることについての「悪い習慣」はありません。

いずれにせよ、構文であろうと意味論であろうと、あなたはそれについて選択の余地はありません。

5
user207421

これを書き直して、最後に戻るようにします。擬似コード:

if a == null throw ...
// else not needed, if this is reached, a is not null
Object b
try {
  b = a.clone
}
catch ...

return b
2
Pablo Pazos

戻り値のnull。例外がキャッチされる可能性があるため、例外が必要な場合がありますが、そのような場合はすでにnullであるかどうかを確認しているため(呼び出しているクラスがクローンをサポートしていることを前提としています)、tryステートメントが失敗することはありません。

tryステートメントが失敗することは決してないことがわかっている方法に関係する入力の詳細を知っている場合、それがあることのポイントは何ですか?常に成功することがわかっている場合は、tryを避けてください(ただし、コードベースの有効期間全体にわたって完全に確信できることはめったにありません)。

いずれにせよ、コンパイラーは残念ながら心の読者ではありません。関数とその入力が表示され、その情報が与えられると、下にあるreturnステートメントが必要になります。

構文を満たし、コンパイルエラーを回避するために最後に余分なreturnステートメントを入れるのは悪い習慣ですか(コメントは到達しません)、またはこのような何かをコーディングするより良い方法がありますか? returnステートメントは不要ですか?

まったく逆に、コンパイラの警告を回避することをお勧めします。たとえば、別のコード行が必要な場合でも。ここで行数についてあまり心配しないでください。テストを通じて機能の信頼性を確立し、次に進みます。 returnステートメントを省略して、1年後にそのコードに戻って、下部のreturnステートメントがコメントの詳細よりも混乱を引き起こすかどうかを判断することを想像してください入力パラメーターについて行うことができる仮定のために省略された理由の詳細。おそらく、returnステートメントの方が扱いやすいでしょう。

つまり、特にこの部分については:

try {
    return a.clone();
} catch (CloneNotSupportedException e) {
   e.printStackTrace();
}
...
//cant be reached, in for syntax
return null;

ここで例外処理の考え方には少し奇妙なことがあると思います。一般に、応答で実行できる意味のある何かがあるサイトでは例外を飲み込みます。

try/catchトランザクションメカニズムとして。 tryがこれらの変更を行い、それらが失敗し、catchブロックに分岐する場合、ロールバックおよびリカバリの一部として応答して(catchブロックにあるものは何でも)プロセス。

この場合、単にスタックトレースを出力してからnullを返すことを強制するだけでは、トランザクション/リカバリの考え方とは異なります。このコードは、エラー処理の責任をgetCloneを呼び出すすべてのコードに転送して、失敗を手動で確認します。 CloneNotSupportedExceptionをキャッチして、それを別のより意味のある例外の形に変換してスローすることを好むかもしれませんが、この場合、単に例外を飲み込んでnullを返したくありません。トランザクション回復サイト。

例外をスローするとこれを回避できる場合、失敗を手動で確認して対処するために、呼び出し元に責任をリークすることになります。

ファイルをロードすると、それが高レベルのトランザクションになります。 try/catch そこ。ファイルをロードするtryingのプロセス中に、オブジェクトのクローンを作成できます。この高レベルの操作(ファイルのロード)のどこかに障害が発生した場合、通常、このトップレベルのトランザクションまで例外をスローする必要がありますtry/catchブロックして、ファイルのロードの失敗から適切に回復できるようにします(クローン作成またはその他のエラーによるものかどうか)。そのため、通常、このような粒度の細かい場所で例外を飲み込んでからnullを返したくありません。これは、例外の多くの価値と目的を損なうためです。代わりに、例外を意味のある方法で処理できるサイトまでずっと伝播したいのです。

2
Dragon Energy

誰もこれについてまだ言及していないので、ここに行きます:

public static final Object ERROR_OBJECT = ...

//...

public Object getClone(Cloneable a) throws TotallyFooException {
Object ret;

if (a == null) 
    throw new TotallyFooException();

//no need for else here
try {
    ret = a.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
    //something went wrong! ERROR_OBJECT could also be null
    ret = ERROR_OBJECT; 
}

return ret;

}

そのため、returnブロック内のtryブロックが嫌いです。

2
rath

最後の段落で述べたように、あなたの例はあなたの質問を説明するのに理想的ではありません:

構文を満たし、コンパイルエラーを回避するために最後に余分なreturnステートメントを入れるのは悪い習慣ですか(コメントは到達しません)、またはこのような何かをコーディングするより良い方法がありますか? returnステートメントは不要ですか?

より良い例は、クローン自体の実装です。

 public class A implements Cloneable {
      public Object clone() {
           try {
               return super.clone() ;
           } catch (CloneNotSupportedException e) {
               throw new InternalError(e) ; // vm bug.
           }
      }
 }

ここで、catch句はneverと入力する必要があります。それでも、構文は何かをスローするか、値を返す必要があります。何かを返すことは意味がないので、InternalErrorを使用して、重大なVM状態を示します。

0
wero