Использование аннотации NotNull в аргументе метода

156

Я только начал использовать @NotNullаннотацию с Java 8 и получил некоторые неожиданные результаты.

У меня есть такой метод:

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

Я написал тест JUnit, передав нулевое значение для аргумента searchList. Я ожидал, что произойдет какая-то ошибка, но она прошла, как будто аннотации не было. Это ожидаемое поведение? Из того, что я понял, это должно было позволить вам пропустить написание стандартного кода проверки на ноль.

Объяснение того, что именно должен делать @NotNull, будет с благодарностью.

DavidR
источник
29
@NotNullэто просто аннотация. Аннотации ничего не делают сами по себе. Им нужен процессор аннотаций во время компиляции или что-то, что обрабатывает его во время выполнения.
Сотириос Делиманолис
Вы запускаете код на сервере приложений (например, используя Arquillian )?
Jabu.10245
1
@SotiriosDelimanolis - Так в чем же смысл, просто предупреждение тому, кто вызывает метод, чтобы не передавать нулевое значение? В этом случае вам все еще нужен код проверки нулевого указателя.
DavidR
1
посмотрите на спящий валидатор
arisalexis
@ jabu.10245 - не использует сервер приложений.
DavidR

Ответы:

183

@Nullableи @NotNullничего не делать самостоятельно. Они должны выступать в качестве инструментов документации.

@NullableАннотация напоминает вам о необходимости ввести проверку NPE , когда:

  1. Вызов методов, которые могут вернуть ноль.
  2. Разыменование переменных (полей, локальных переменных, параметров), которые могут быть нулевыми.

@NotNullАннотаций, собственно, явный контракт объявив следующее:

  1. Метод не должен возвращать ноль.
  2. Переменная (например, поля, локальные переменные и параметры) не может содержать нулевое значение.

Например, вместо того, чтобы писать:

/**
 * @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. Проверка бина
  2. NotNull.java
  3. Constraint.java
  4. ConstraintValidator.java
justAnotherUser ...
источник
3
Итак, просто чтобы прояснить часть 2 части NotNull, на самом деле она должна сказать «не должен», а не «не может», поскольку не может быть принудительно применена? Или, если это можно применить во время выполнения, как бы вы поступили?
DavidR
Да, это "не должно" ... реализация метода должна обеспечивать выполнение контракта.
justAnotherUser ...
1
В качестве альтернативы в Java 8 Optionalможно было бы использовать вместо @Nullвозвращаемых значений и перегрузку метода вместо @Nullсписков параметров: dolszewski.com/java/java-8-optional-use-cases
Чад К
13
Я считаю , что путаница происходит из Java документа в NotNull аннотации: * The annotated element must not be {@code null}. * Accepts any type.и я думаю , что обязательно слово должно быть заменено должно , но опять же это зависит от того, как вы читаете это. Определенно, было бы неплохо дать еще некоторые разъяснения
Джулиан,
@Julian Я думаю , что обязательно является правильным термином , поскольку это правило, а не рекомендация. Если вы используете аннотацию там, где вы не должны проходить, nullно это будет разрешено, вы используете аннотацию неправильно. Термин не означает, что он подтвержден. Однако намек на то, что он не подтвержден, не повредит. Если вы хотите добавить автоматическую проверку, вы можете использовать некоторые внешние инструменты. Например, IntelliJ IDE имеет встроенную поддержку для вставки нулевых проверок.
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
Просто совет. Вы также можете написать такие задания в одну строку: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
да .. в начале метода ... это только одна из реализаций валидации, могут быть и другие ...
Наруто
Хорошо. Но это значение того, что делает этот код, не изменится, есть ли у меня аннотация @NotNull в аргументе param?
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));
    }
}

Вы можете сделать что-то очень похожее, чтобы проверить @NotNull.

WesternGun
источник
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));

}
Жюльен Фениу
источник