次の一般的な形式の関数を考えます。
Foo findFoo(Collection<Foo> foos, otherarguments)
throws ObjectNotFoundException {
for(Foo foo : foos){
if(/* foo meets some condition*/){
return foo;
}
}
throw new ObjectNotFoundException();
}
たとえば、具体的なケースは次のとおりです。
User findUserByName(Collection<User> users, String name)
throws ObjectNotFoundException {
for(User user : users){
if(user.getName().equals(name)){
return user;
}
}
throw new ObjectNotFoundException();
}
オブジェクトが見つからない場合、これらの関数は例外をスローします。この目的のためにカスタム例外クラスを作成できます(例ではObjectNotFoundException
)が、既存のクラスを使用することをお勧めします。しかし、標準Javaライブラリでこの意味を持つ例外クラスを見つけることができませんでした。ここで使用できる標準例外があるかどうか知っていますか?
ここで使用できる標準的な例外があるかどうか知っていますか?
couldを使用できる例外がいくつかあります(例:NoSuchElementException
またはIllegalArgumentException
)が、答えは実際に伝える意味に依存します。
NoSuchElementException
は、シーケンスまたは列挙をステップスルーするときに使用される傾向があります。ここにあるのはルックアップです。
IllegalArgumentException
は、引数にエラーがあることを意味する傾向がありますが、この場合、呼び出し元の仮定が間違っているか、アプリケーションロジックに固有のものである可能性があります。
カスタム例外により、例外の意味を(javadocsで)正確に言うことができます。また、checked...と宣言することもできます(適切な場合)。
(しかし、 UnknownUserException
を使用するように誘惑されないでください。それは恐ろしく間違っているでしょう。javadocを読んでください!)
また、特にルックアップの失敗がアプリケーションで非常に一般的な(例外ではない)イベントである可能性が高い場合は、null
を返すことも検討する価値があります。 ただし、、null
を返すことのマイナス面は、呼び出し元がnull
をチェックする必要があること、または予期しないNullPointerException
sを危険にさらすことです。 。実際、null
の過剰使用は、例外の過剰使用よりも悪いと主張します。前者はアプリケーションの信頼性を低下させる可能性がありますが、後者はパフォーマンスに「唯一の」悪影響を及ぼすだけです。
Java 8以降の場合、Optional
を返すのは、null
を返すよりも明確な選択です。
これらのことにおいて、教義を超えて見て、実際の文脈が必要とするものに基づいて心を決めることが重要です。
例外は、例外的な動作をマークするために作成されます。私の意見では、オブジェクトが見つからない状況は例外ではありません。ユーザーが見つからない場合、nullを返すようにメソッドを書き直します。
User findUserByName(Collection<User> users, String name) {
for(User user : users){
if(user.getName().equals(name)){
return user;
}
}
return null;
}
これは、多くのJavaコレクションの標準的な動作です。たとえば、 http://docs.Oracle.com/javase/7/docs/api/Java/util/Map.html指定されたキーを持つエントリがマップにない場合、#get(Java.lang.Object) はnullを返します。
プログラムロジックの例外に依存しないようにしてください。
ここではIllegalArgumentException
が使用されることもありますが、独自の例外を使用することはまったく問題ありません。
余談ですが、キーとしてString name
と値としてUser
を使用してマップを使用することをお勧めします。この場合、コレクションを反復処理する必要はなく、コレクション内に同じ名前の2人のユーザーがいることを防ぎます。マップを使用したくない場合は、少なくとも次のようにNullPointerException
に対して防御します。
User findUserByName(Collection<User> users, String name) throws ObjectNotFoundException
{
if (name == null)
{
throw new IllegalArgumentException("name parameter must not be null");
}
if (users == null)
{
throw new IllegalArgumentException("Collection of users must not be null");
}
for(User user : users)
{
if(name.equals(user.getName()))
{
return user;
}
}
throw new ObjectNotFoundException("Unable to locate user with name: " + name);
}
Java 8の場合、この使用例ではOptionalを使用することをお勧めします。
Optional<User> findUserByName(Collection<User> users, String name){
Optional<User> value = users
.stream()
.filter(a -> a.equals(name))
.findFirst();
}
また、これにより、値が見つからない場合にオプションが空になる可能性があることを呼び出し元に非常に明確にします。本当に例外をスローしたい場合は、OptionalでorElseThrows
を使用してそれを達成できます。
メソッドの文書化されたインターフェイスコントラクトに依存します。
メソッドのドキュメントに、name
引数mustが既存のユーザーの名前に対応していると記載されている場合、IllegalArgumentException
名前が見つからない場合は、呼び出し元がユーザーに対応しない名前を渡すことでメソッドの要件に違反したことを意味するため。
メソッドが名前が既存のユーザーに対応する必要があると言っていない場合、不明な名前を渡すことはエラーではなく、例外をスローするべきではありません。この状況では、null
を返すことが適切です。
findUserByName
メソッドは、基本的に Map.get
メソッド。指定されたキーが見つからない場合にnull
を返します。