Javaコンストラクタースタイル:パラメーターがnullでないことを確認
いくつかのパラメーターを受け入れるクラスがあり、どのパラメーターもnull
にできない場合のベストプラクティスは何ですか?
以下は明らかですが、例外は少し不明確です。
public class SomeClass
{
public SomeClass(Object one, Object two)
{
if (one == null || two == null)
{
throw new IllegalArgumentException("Parameters can't be null");
}
//...
}
}
ここでは、例外によってどのパラメーターがnullであるかがわかりますが、コンストラクターはかなり見苦しくなりました。
public class SomeClass
{
public SomeClass(Object one, Object two)
{
if (one == null)
{
throw new IllegalArgumentException("one can't be null");
}
if (two == null)
{
throw new IllegalArgumentException("two can't be null");
}
//...
}
ここでは、コンストラクターはよりきれいですが、コンストラクターコードは実際にはコンストラクター内にありません。
public class SomeClass
{
public SomeClass(Object one, Object two)
{
setOne(one);
setTwo(two);
}
public void setOne(Object one)
{
if (one == null)
{
throw new IllegalArgumentException("one can't be null");
}
//...
}
public void setTwo(Object two)
{
if (two == null)
{
throw new IllegalArgumentException("two can't be null");
}
//...
}
}
これらのスタイルのうちどれが最適ですか?
または、より広く受け入れられている代替手段はありますか?
2番目または3番目。
APIのユーザーに正確に何が悪かったのかを伝えるからです。
冗長性を抑えるには、commons-langのValidate.notNull(obj, message)
を使用します。したがって、コンストラクタは次のようになります。
public SomeClass(Object one, Object two) {
Validate.notNull(one, "one can't be null");
Validate.notNull(two, "two can't be null");
...
}
セッターにチェックを入れることも許容され、同じ冗長性のコメントがあります。セッターがオブジェクトの一貫性を維持する役割も持っている場合は、3番目のセッターも選択できます。
前提条件チェックを容易にするために設計された多くのライブラリーのいずれかを使用できます。 Google Guava の多くのコードは com.google.common.base.Preconditions
正しい引数と状態を確認するために、独自のメソッドの開始時に呼び出される単純な静的メソッド。これにより、
if (count <= 0) { throw new IllegalArgumentException("must be positive: " + count); }
よりコンパクトに置き換えられる
checkArgument(count > 0, "must be positive: %s", count);
checkNotNull
つまり グアバ内で広く使用されている です。次のように書くことができます:
import static com.google.common.base.Preconditions.checkNotNull;
//...
public SomeClass(Object one, Object two) {
this.one = checkNotNull(one);
this.two = checkNotNull(two, "two can't be null!");
//...
}
ほとんどのメソッドは、エラーメッセージを受け取らないか、固定エラーメッセージを受け取るか、可変引数付きのテンプレートエラーメッセージを受け取るためにオーバーロードされます。
IllegalArgumentException
対NullPointerException
で
元のコードはIllegalArgumentException
引数にnull
をスローしますが、グアバのPreconditions.checkNotNull
は代わりにNullPointerException
をスローします。
Effective Java 2nd Edition:項目60:標準例外の使用を推奨する)からの引用です:
間違いなく、すべての誤ったメソッド呼び出しは、無効な引数または無効な状態に要約されますが、他の例外は、特定の種類の無効な引数および状態に対して標準的に使用されます。呼び出し元がnull値が禁止されているパラメーターで
null
を渡す場合、慣例により、NullPointerException
ではなくIllegalArgumentException
がスローされます。
NullPointerException
は、null
参照のメンバーにアクセスするときだけに予約されていません。引数がnull
である場合、それが無効な値である場合にスローするのはかなり標準です。
System.out.println("some string".split(null));
// throws NullPointerException
古い質問。別の新しい回答(すでに別のコメントで言及されていますが、独自の回答に値すると思います)。
Java 7は、誰もが使用できるAPIにJava.lang.Objects.requireNonNull()
を追加しました。したがって、nullのすべての引数をチェックすると、次のような短いリストになります。
this.arg1 = Objects.requireNonNull(arg1, "arg1 must not be null");
this.arg2 = Objects.requireNonNull(arg2, "arg2 must not be null");
サイドノート:
- 2つの引数を逆にしないようにしてください-2番目の引数は、1番目の引数がヌルの場合にスローされるNPEに使用されるメッセージです(逆にすると、まあ、あなたのチェックは決して失敗しません)
- 別のベストプラクティス:可能であれば、すべてのクラスメンバーをfinalにします(したがって、確実に、オブジェクトが正常に作成された場合、そのすべてのメンバーはnullではなく、時間とともに変化しません)
私はユーティリティメソッドを持っています:
public static <T> T checkNull(String message, T object) {
if(object == null) {
throw new NullPointerException(message);
}
return object;
}
次のような割り当てで使用できるように、オブジェクトを返すようにします。
public Constructor(Object param) {
this.param = checkNull("Param not allowed to be null", param);
}
編集:サードパーティのライブラリを使用するための提案については、特にGoogleの前提条件は私のコードよりも上記をさらに行います。ただし、これがプロジェクトにライブラリを含める唯一の理由であれば、私はためらいます。この方法は単純すぎます。
上記の回答はすべて有効かつ合理的ですが、nullをチェックする必要はないかもしれないことを指摘するのは良いことだと思います。 (OP以外の読者がこの質問を独断的なものとみなすかもしれないと仮定して)
テスト可能性に関するMisko Heveryブログから: アサートするかしないか
Java-Guava対Apache Commons対Spring Framework対Plain Java Assertsで前提条件をチェックする方法の比較
public static void fooSpringFrameworkAssert(String name, int start, int end) {
// Preconditions
Assert.notNull(name, "Name must not be null");
Assert.isTrue(start < end, "Start (" + start + ") must be smaller than end (" + end + ")");
// Do something here ...
}
public static void fooApacheCommonsValidate(String name, int start, int end) {
// Preconditions
Validate.notNull(name, "Name must not be null");
Validate.isTrue(start < end, "Start (%s) must be smaller than end (%s)", start, end);
// Do something here ...
}
public static void fooGuavaPreconditions(String name, int start, int end) {
// Preconditions
Preconditions.checkNotNull(name, "Name must not be null");
Preconditions.checkArgument(start < end, "Start (%s) must be smaller than end (%s)", start, end);
// Do something here ...
}
public static void fooPlainJavaAsserts(String name, int start, int end) {
// Preconditions
assert null != name : "Name must not be null";
assert start < end : "Start (" + start + ") must be smaller than end (" + end + ")";
// Do something here ...
}
これはこの記事の要約です: http://www.sw-engineering-candies.com/blog-1/comparison-of-ways-to-check-preconditions-in-Java
未チェックの例外をスローする代わりに、assert
を使用します。そうでない場合、コンストラクターが不正な値を処理しないという事実を呼び出し元に認識させるために、チェック例外をスローします。
最初の2つのソリューションの違い-詳細なエラーメッセージが必要ですか、どのパラメータが失敗したか、または不正な引数のためにインスタンスを作成できなかったことを知るのに十分であるかを知る必要がありますか?
2番目と3番目の例では、両方のパラメーターがnullであったことを正しく報告できないことに注意してください。
ところで-私は(1)のバリエーションに投票します:
if (one == null || two == null) {
throw new IllegalArgumentException(
String.format("Parameters can't be null: one=%s, two=%s", one, two));
}
実行時チェックの追加またはインプレースの静的分析の注釈も役立ちます。
たとえば、FindBugsは@NonNullアノテーションを提供します。
public SomeClass(@NonNull Object one、@NonNull Object two){
検証する必要があるすべてのコンストラクター引数をとるメソッドを単純に持つことができます。このメソッドは、無効な引数に応じて特定のメッセージで例外をスローします。コンストラクターはこのメソッドを呼び出し、渡されると値を初期化します。
Javaのassert
に組み込まれているものについて話したと思います。私の意見では、それを使用するのは本当に良い考えではありません。コマンドラインパラメータを使用してオン/オフを切り替えることができるため。 したがって、一部の人はプライベートメソッドで使用することだけが許容されると言います。
私のメンターは、車輪を再発明しないように言っています!彼らのアドバイスは、ライブラリを使用することです。それらは(おそらく)適切に設計およびテストされています。もちろん、高品質のライブラリを確実に入手するのはあなたの責任です。
他の人は、エンタープライズppl-ある意味では-が間違っていて、単純なタスクのために必要以上に多くの依存関係を導入していると言っています。私もその点を受け入れることができます...しかし、ここに私の最新の経験があります:
最初に、nullパラメーターをチェックするための独自のプライベートメソッドを作成しました。それは退屈で冗長です。 Utilityクラスに入れる必要があることは知っています。しかし、なぜ誰かがすでにそれを行ったのに、そもそもそれを書くべきなのでしょうか?単体テストを作成せずに時間を節約し、既存のものを設計できます。あなたが運動したり学びたいのでなければ、そうすることはお勧めしません。
私は最近グーグルのグアバを使い始めましたが、Apache commonsと一緒に使い始めたら、その1つの方法だけに使うことはないでしょう。あなたはそれをますます発見し、使用するでしょう。最後に、これによりコードが短くなり、読みやすくなり、一貫性と保守性が向上します。
ところで:あなたの目的に応じて、私は上記のライブラリのいずれかを使用して2または3で行くでしょう...