メソッド引数でのNotNullアノテーションの使用


156

私はちょうど使い始めました @NotNullJava 8でアノテーションを予期しない結果が得られました。

私はこのような方法を持っています:

public List<Found> findStuff(@NotNull List<Searching> searchingList) {
    ... code here ...
}

引数searchListのnull値を渡すJUnitテストを作成しました。なんらかのエラーが発生することを予期していましたが、アノテーションが存在しないかのようにエラーが発生しました。これは予想される動作ですか?私が理解したところ、これはボイラープレートのnullチェックコードの記述をスキップできるようにするためです。

@NotNullが正確に何をすることになっているのかについての説明をいただければ幸いです。


29
@NotNull単なる注釈です。注釈はそれ自体では何もしません。コンパイル時に注釈プロセッサ、または実行時に注釈プロセッサを処理するものが必要です。
Sotirios Delimanolis

アプリケーションサーバー内でコードを実行していますか(たとえば、Arquillianを使用していますか)?
jabu.10245

1
@SotiriosDelimanolis-では、要点は何ですか?null値を渡さないようにメソッドを呼び出す人への警告だけですか?その場合でも、ヌルポインター検証コードが必要です。
DavidR

1
hibernateバリデーターを見てください
arisalexis

@ jabu.10245-アプリケーションサーバーを使用しません。
DavidR

回答:


183

@Nullableそして、@NotNull自分では何もしません。それらはドキュメンテーションツールとして機能することになっています。

@Nullable注釈は、NPEチェックを導入する必要性について、あなたを思い出させます:

  1. nullを返すことができるメソッドの呼び出し。
  2. nullの可能性がある変数(フィールド、ローカル変数、パラメーター)の逆参照。

@NotNull注釈は、実際には、以下のことを宣言し、明示的な契約です。

  1. メソッドはnullを返しません。
  2. (フィールド、ローカル変数、およびパラメータのような)変数がないことができ ないはず null値を保持します。

たとえば、書く代わりに:

/**
 * @param aX should not be null
 */
public void setX(final Object aX ) {
    // some code
}

以下を使用できます。

public void setX(@NotNull final Object aX ) {
    // some code
}

さらに、@NotNull多くの場合、ConstraintValidatorsによってチェックされます(例:春および休止状態)。

@NotNullので、注釈は独自のを検証しない注釈の定義は、いずれかを提供していませんConstraintValidatorタイプの参照を。

詳細については、以下を参照してください。

  1. Beanの検証
  2. NotNull.java
  3. Constraint.java
  4. ConstraintValidator.java

3
それで、NotNullパートのパート2を明確にするために、ベッドを強制できないので、実際には、「できない」ではなく、「すべきでない」と言う必要がありますか?または、実行時に適用できる場合、どうすればよいですか?
DavidR

はい、「すべきではない」...メソッドの実装は契約を強制する必要があります。
justAnotherUser ... 2015

1
または、Java 8では、戻り値のOptional代わりに使用でき、パラメーターリストの@Null代わりにメソッドのオーバーロード@Nullを使用できます。dolszewski.com
Chad K

13
私は混乱がNOTNULL注釈のJavaのドキュメントから来ていると信じて: * The annotated element must not be {@code null}. * Accepts any type.と私は思う必見のに単語を交換しなければならないはずが、再び、それはあなたがそれを読んでどのように依存します。明らかに、もう少し明確にしておくとよいでしょう
Julian

@Julian 規則であり、推奨事項ではないため、正しい用語である必要があると思います。あなたは、アノテーションを使用する場合はならない渡すnullが、それは許されるが、あなたは注釈間違っを使用しています。この用語は、検証されたことを意味するものではありません。ただし、検証されていないというヒントは害になりません。自動検証を追加する場合は、いくつかの外部ツールを使用できます。たとえば、IntelliJ IDEには、nullチェックを挿入するための組み込みサポートがあります。
JojOatXGME

27

上記のよう@NotNullに、それ自体では何もしません。良い使い方は@NotNullそれをObjects.requireNonNull

public class Foo {
    private final Bar bar;

    public Foo(@NotNull Bar bar) {
        this.bar = Objects.requireNonNull(bar, "bar must not be null");
    }
}

6
ほんのヒント。また、1つのラインで、このような割り当てを書くことができます:this.bar = Objects.requireNonNull(bar, "bar must not be null");
lolung

ヒントをありがとう@lolung-私は今、あなたのコメントに基づいて上記のコードを省略して更新しました
ボリウッド


6

SO @NotNullは単なるタグです...それを検証したい場合は、hibernate validator jsr 303のようなものを使用する必要があります

ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
 Set<ConstraintViolation<List<Searching>> violations = validator.validate(searchingList);

これをメソッドの最初のどこに置くのですか?
DavidR

はい..メソッドの最初に...これは検証の実装の1つにすぎません。他にもある可能性があります...
Naruto

OK。しかし、param引数に@NotNullアノテーションがあるかどうかに関係なく、そのコードが何を行うかのこの重要性は変わりませんか?
DavidR

これで、すべての違反がセットに含まれています。そのサイズを確認し、ゼロより大きい場合は、メソッドから戻ります。
ナルト


2

これを行って、独自の検証アノテーションとバリデーターを作成します。

ValidCardType.java(メソッド/フィールドに付ける注釈)

@Constraint(validatedBy = {CardTypeValidator.class})
@Documented
@Target( { ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidCardType {
    String message() default "Incorrect card type, should be among: \"MasterCard\" | \"Visa\"";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

そして、チェックをトリガーするバリデーター CardTypeValidator.java

public class CardTypeValidator implements ConstraintValidator<ValidCardType, String> {
    private static final String[] ALL_CARD_TYPES = {"MasterCard", "Visa"};

    @Override
    public void initialize(ValidCardType status) {
    }
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return (Arrays.asList(ALL_CARD_TYPES).contains(value));
    }
}

checkとよく似たことができます@NotNull


0

テストでメソッドの検証をテストするには、@ Beforeメソッドでプロキシをラップする必要があります。

@Before
public void setUp() {
    this.classAutowiredWithFindStuffMethod = MethodValidationProxyFactory.createProxy(this.classAutowiredWithFindStuffMethod);
}

MethodValidationProxyFactoryを次のように使用します:

import org.springframework.context.support.StaticApplicationContext;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;

public class MethodValidationProxyFactory {

private static final StaticApplicationContext ctx = new StaticApplicationContext();

static {
    MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
    processor.afterPropertiesSet(); // init advisor
    ctx.getBeanFactory()
            .addBeanPostProcessor(processor);
}

@SuppressWarnings("unchecked")
public static <T> T createProxy(T instance) {

    return (T) ctx.getAutowireCapableBeanFactory()
            .applyBeanPostProcessorsAfterInitialization(instance, instance.getClass()
                    .getName());
}

}

次に、テストを追加します。

@Test
public void findingNullStuff() {
 assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> this.classAutowiredWithFindStuffMethod.findStuff(null));

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.