次のコードを検討してください。
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ステートメントは不要ですか?
追加のreturnステートメントを使用しない、より明確な方法は次のとおりです。 CloneNotSupportedException
もキャッチしませんが、呼び出し元に渡します。
if (a != null) {
try {
return a.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
throw new TotallyFooException();
ほとんどの場合、最初に持っているものよりも簡単な構文で終わる順序をいじることは可能です。
間違いなく到達できます。 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必ずしもマップにキーのマッピングが含まれていないことを示すわけではありません。マップがキーを明示的にマップすることも可能ですnull。
containsKey
操作を使用して、これら2つのケースを区別できます。
この場合、null
は、valuenull
が見つかって返されたこと、またはハッシュマップに要求されたキーが含まれていないことを示すことができます。
構文を満たし、コンパイルエラーを回避するために、最後に余分なreturnステートメントを入れることは悪い習慣ですか(説明されていないコメントで)
return null
は、到達不能ブランチの終端にとって悪い習慣だと思います。 RuntimeException
( AssertionError
も許容される)をスローすることをお勧めします。不明な状態です。開発者が何かを見逃しているため、このようなものがほとんどです(上記のように)(オブジェクトはnullでなく、クローンできない可能性があります)。
私が作る可能性が高いので、コードが到達不能であることを非常に確信していない限り(たとえばSystem.exit()
の後)、 InternalError
を使用しない可能性が高いVMよりも間違い。
カスタム例外(TotallyFooException
など)を使用するのは、その「到達不能な行」に到達することが、その例外をスローする他の場所と同じことを意味する場合だけです。
CloneNotSupportedException
をキャッチしたため、コードで処理できます。しかし、それをキャッチした後は、文字通り、関数の最後に達したときに何をすべきかわかりません。これは、それを処理できなかったことを意味します。したがって、この場合はコードのにおいであり、私の見解では、CloneNotSupportedException
をキャッチすべきではないということです。
Objects.requireNonNull()
を使用して、パラメーターaがnullでないかどうかを確認したいと思います。したがって、コードを読んだときに、パラメーターがnullであってはならないことは明らかです。
そして、チェックされた例外を避けるために、CloneNotSupportedException
をRuntimeException
として再スローします。
どちらの場合も、このようなことが起こらない、またはそうでない理由で、ニーステキストを追加できます。
public Object getClone(Object a) {
Objects.requireNonNull(a);
try {
return a.clone();
} catch (CloneNotSupportedException e) {
throw new IllegalArgumentException(e);
}
}
このような状況では、私は書くだろう
_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) ; } }
}
_
上記の例は有効で非常にJavaです。ただし、その戻り値を処理する方法に関するOPの質問に対処する方法は次のとおりです。
public Object getClone(Cloneable a) throws CloneNotSupportedException {
return a.clone();
}
a
がnullであるかどうかを確認してもメリットはありません。 NPEになります。スタックトレースの印刷も役に立ちません。スタックトレースは、処理場所に関係なく同じです。
役に立たないヌルテストや役に立たない例外処理でコードを無駄にすることには利点がありません。ジャンクを削除することにより、返品の問題は議論の余地があります。
(OPには例外処理にバグが含まれていることに注意してください。これが復帰が必要な理由です。OPは私が提案する方法を間違えなかったでしょう。)
構文の悪い習慣を満たすためだけにreturnステートメントを持っていますか?
他の人が述べたように、あなたの場合、これは実際には適用されません。
しかし、質問に答えるために、Lintタイプのプログラムはそれを理解していません。私は2つの異なるものがスイッチステートメントでこれを争うのを見ました。
switch (var)
{
case A:
break;
default:
return;
break; // Unreachable code. Coding standard violation?
}
notがブレークすることはコーディング標準違反であると不満を言いました。もう1つは、到達不能なコードであるためhavingであると不満を述べました。
このことに気づいたのは、2人の異なるプログラマーが、その日に実行したコードアナライザーに応じて、ブレークを追加、削除、追加、削除してコードを再チェックし続けたためです。
このような状況に陥った場合は、その1つを選んで異常にコメントしてください。それが最良かつ最も重要なポイントです。
「構文を満たすだけ」ではありません。すべてのコードパスがリターンまたはスローにつながることは、言語のセマンティック要件です。このコードは準拠していません。例外がキャッチされた場合、次の返品が必要です。
それについて、またはコンパイラー全般を満足させることについての「悪い習慣」はありません。
いずれにせよ、構文であろうと意味論であろうと、あなたはそれについて選択の余地はありません。
これを書き直して、最後に戻るようにします。擬似コード:
if a == null throw ...
// else not needed, if this is reached, a is not null
Object b
try {
b = a.clone
}
catch ...
return b
戻り値の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を返したくありません。これは、例外の多くの価値と目的を損なうためです。代わりに、例外を意味のある方法で処理できるサイトまでずっと伝播したいのです。
誰もこれについてまだ言及していないので、ここに行きます:
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
ブロックが嫌いです。
最後の段落で述べたように、あなたの例はあなたの質問を説明するのに理想的ではありません:
構文を満たし、コンパイルエラーを回避するために最後に余分な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状態を示します。