Використання NotNull Annotation в аргументі методу


156

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

У мене такий метод:

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

Я написав тест JUnit, передаючи нульове значення для аргументу searchList. Я очікував, що трапиться якась помилка, але вона пройшла так, ніби анотації там не було. Це очікувана поведінка? З того, що я зрозумів, це дозволило вам пропустити написання нульового коду перевірки котлована.

Пояснення того, що саме слід робити @NotNull, було б дуже вдячно.


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. Обмеження.java
  4. ConstraintValidator.java

3
Отже, щоб уточнити частину 2 частини NotNull, насправді вона повинна говорити "не слід", не "не можу", оскільки вона не може примусово застосовувати? Або якщо це може бути застосовано під час виконання, як би ви це зробили?
DavidR

Так, його "не повинно" ... реалізація методу повинна забезпечити виконання контракту.
justAbodyUser ...

1
Крім того, у Java 8 Optionalможна використовувати замість @Nullповернених значень, а метод перевантажувати замість @Nullсписків параметрів: dolszewski.com/java/java-8-optional-use-cases
Чад K

13
Я вважаю , що плутанина відбувається з Java документа в NotNull анотації: * The annotated element must not be {@code null}. * Accepts any type.і я думаю , що обов'язково слово повинно бути замінено має , але знову ж це залежить від того, як ви читаєте це. Однозначно, було б добре мати ще якісь уточнення
Julian

@Julian Я думаю , що обов'язково є правильним терміном , оскільки це правило, а не рекомендація. Якщо ви використовуєте примітку там, де ви не повинні проходити, nullале це буде дозволено, ви використовуєте примітку неправильно. Термін не означає, що він підтверджений. Однак натяк на те, що він не підтверджений, не зашкодив би. Якщо ви хочете додати автоматичну перевірку, ви можете використовувати деякі зовнішні інструменти. Наприклад, IDE IntelliJ має вбудовану підтримку для введення нульових перевірок.
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

ТАК @NotNull - це просто тег ... Якщо ви хочете перевірити його, ви повинні використовувати щось на зразок сплячого валідатора 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.


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));

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.