Używanie NotNull Annotation w argumencie metody


156

Właśnie zacząłem używać @NotNulladnotacji w Javie 8 i otrzymałem nieoczekiwane wyniki.

Mam taką metodę:

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

Napisałem test JUnit przekazujący wartość null dla argumentu searchList. Spodziewałem się jakiegoś błędu, ale wyglądało to tak, jakby nie było adnotacji. Czy to oczekiwane zachowanie? Z tego, co zrozumiałem, miało to umożliwić pominięcie pisania standardowego kodu kontrolnego zerowego.

Bardzo mile widziane byłoby wyjaśnienie, co dokładnie ma robić @NotNull.


29
@NotNullto tylko adnotacja. Adnotacje same w sobie nic nie robią. Potrzebują procesora adnotacji w czasie kompilacji lub czegoś, co przetwarza go w czasie wykonywania.
Sotirios Delimanolis

Czy uruchamiasz kod na serwerze aplikacji (na przykład używając Arquilliana )?
jabu.10245

1
@SotiriosDelimanolis - Więc o co chodzi, tylko ostrzeżenie dla każdego wywołującego metodę, aby nie przekazywała wartości null? W takim przypadku nadal potrzebujesz kodu walidacji wskaźnika zerowego.
DavidR

1
spójrz na walidator hibernacji
arisalexis

@ jabu.10245 - Nie używasz żadnego serwera aplikacji.
DavidR

Odpowiedzi:


183

@Nullablei @NotNullnic nie robią samodzielnie. Mają pełnić rolę narzędzi dokumentacji.

@NullableAdnotacja przypomina o konieczności wprowadzenia sprawdzenie NPE gdy:

  1. Wywołanie metod, które mogą zwracać null.
  2. Wyłuskiwanie zmiennych (pola, zmienne lokalne, parametry), które mogą być zerowe.

@NotNullAdnotacja jest, faktycznie, wyraźna umowa oświadczając, co następuje:

  1. Metoda nie powinna zwracać wartości null.
  2. Zmienna (taka jak pola, zmienne lokalne i parametry) nie może mieć wartości null.

Na przykład zamiast pisać:

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

Możesz użyć:

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

Dodatkowo @NotNulljest często sprawdzany przez ConstraintValidators (np. Wiosną i hibernacją).

@NotNullAdnotacja nie robi żadnej walidacji na własną rękę, ponieważ definicja adnotacja nie dostarcza żadnych ConstraintValidatoroznaczenie typu.

Więcej informacji:

  1. Walidacja fasoli
  2. NotNull.java
  3. Constraint.java
  4. ConstraintValidator.java

3
Tak więc, aby wyjaśnić część 2 części NotNull, naprawdę powinno być napisane „nie powinno”, a nie „nie może”, ponieważ nie może być wymuszone? Lub jeśli można to wymusić w czasie wykonywania, jak byś się do tego zabrał?
DavidR

Tak, to "nie powinno" ... implementacja metody powinna egzekwować kontrakt.
justAnotherUser ...

1
Alternatywnie, w Javie 8, Optionalmoże być używane zamiast @Nullzwracanych wartości, a przeciążanie metod zamiast @Nullw listach parametrów: dolszewski.com/java/java-8-optional-use-cases
Chad K

13
Wierzę, że zamieszanie pochodzi z doc java adnotacji NotNull: * The annotated element must not be {@code null}. * Accepts any type.i myślę koniecznością słowo należy zastąpić powinien jednak ponownie to zależy od tego, jak go odczytać. Zdecydowanie przydałoby się więcej wyjaśnień
Julian

@Julian Myślę, że musi to być właściwy termin, ponieważ jest to reguła, a nie zalecenie. Jeśli używasz adnotacji, w której nie powinieneś zdawać, nullale byłoby to dozwolone, używasz adnotacji nieprawidłowo. Termin nie oznacza, że ​​jest on zatwierdzony. Jednak wskazówka, że ​​nie jest sprawdzona, nie zaszkodzi. Jeśli chcesz dodać automatyczną walidację, możesz użyć niektórych narzędzi zewnętrznych. Na przykład IntelliJ IDE ma wbudowaną obsługę wprowadzania sprawdzeń zerowych.
JojOatXGME

27

Jak wspomniano powyżej, @NotNullsam nie robi nic. Dobrym sposobem na użycie @NotNullbyłoby użycie go zObjects.requireNonNull

public class Foo {
    private final Bar bar;

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

6
Tylko wskazówka. Możesz również napisać takie zadania w jednej linii:this.bar = Objects.requireNonNull(bar, "bar must not be null");
lolung

Dzięki za wskazówkę @lolung - zaktualizowałem powyższy kod wycięty na podstawie Twojego komentarza.
Bollywood


6

SO @NotNull jest tylko tagiem ... Jeśli chcesz go zweryfikować, musisz użyć czegoś takiego jak hibernacja walidatora jsr 303

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

Gdzie mam to umieścić na początku metody?
DavidR

tak .. na początku metody ... to tylko jedna z implementacji walidacji, mogą być też inne ...
Naruto

Dobrze. Ale to znaczenie tego, co robi ten kod, nie zmieni się, niezależnie od tego, czy mam adnotację @NotNull w argumencie param?
DavidR

Teraz masz całe naruszenie w zestawie, sprawdź jego rozmiar, jeśli jest większy niż zero, a następnie wróć z metody.
Naruto


2

Robię to, aby utworzyć własną adnotację walidacyjną i walidator:

ValidCardType.java(adnotacja do umieszczenia na metodach / polach)

@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 {};
}

A walidator uruchamiający czek 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));
    }
}

Możesz zrobić coś bardzo podobnego, aby sprawdzić @NotNull.


0

Aby przetestować walidację metody w teście, musisz opakować ją jako proxy w metodę @Before.

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

Z MethodValidationProxyFactory jako:

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

}

A następnie dodaj swój test:

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

}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.