235

I just started using the @NotNull annotation with Java 8 and getting some unexpected results.

I have a method like this:

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

I wrote a JUnit test passing in the null value for the argument searchingList. I was expecting some type of error to happen but it went through as though the annotation was not there. Is this expected behavior? From what I understood, this was to allow you to skip writing the boilerplate null check code.

An explanation of what exactly @NotNull is supposed to do would be greatly appreciated.

DavidR
  • 6,622
  • 13
  • 56
  • 70
  • 47
    `@NotNull` is just an annotation. Annotations do nothing on their own. They need an annotation processor at compile time, or something that processes it at runtime. – Sotirios Delimanolis Dec 04 '15 at 17:30
  • Are you running the code inside an application server (for example using [Arquillian](http://arquillian.org/))? – jabu.10245 Dec 04 '15 at 17:34
  • 2
    @SotiriosDelimanolis - So then what is the point, just a warning to anyone calling the method not to pass a null value? In which case you still need the null pointer validation code. – DavidR Dec 04 '15 at 17:34
  • 1
    look at hibernate validator – arisalexis Dec 04 '15 at 17:36
  • @jabu.10245 - Not using any application server. – DavidR Dec 04 '15 at 17:36
  • @arisalexis - I have the org.hibernate:hibernate-validator:5.2.2.Final in my classpath. – DavidR Dec 04 '15 at 17:37
  • Then that's the answer. See [this tutorial](https://docs.oracle.com/javaee/7/tutorial/bean-validation003.htm). You need some sort of JavaEE container (CDI for instance) for it to work. Or maybe Hibernate Validation, like the comments suggest. Haven't tried that though. – jabu.10245 Dec 04 '15 at 17:40
  • just including it is not enough! "someone" needs to activate it – arisalexis Dec 04 '15 at 17:41
  • @arisalexis - I am using Spring and do have a LocalValidatorFactoryBean in my Application class which I believe should expose it for use? – DavidR Dec 04 '15 at 17:50

8 Answers8

280

@Nullable and @NotNull do nothing on their own. They are supposed to act as Documentation tools.

The @Nullable Annotation reminds you about the necessity to introduce an NPE check when:

  1. Calling methods that can return null.
  2. Dereferencing variables (fields, local variables, parameters) that can be null.

The @NotNull Annotation is, actually, an explicit contract declaring the following:

  1. A method should not return null.
  2. A variable (like fields, local variables, and parameters) cannot should not hold null value.

For example, instead of writing:

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

You can use:

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

Additionally, @NotNull is often checked by ConstraintValidators (e.g. in Spring and Hibernate).

The @NotNull annotation doesn't do any validation on its own because the annotation definition does not provide any ConstraintValidator type reference.


For more info see:

  1. Bean validation
  2. Annotation Type NotNull
  3. Annotation Type Constraint
  4. Interface ConstraintValidator
informatik01
  • 16,038
  • 10
  • 74
  • 104
Lucas Oliveira
  • 3,357
  • 1
  • 16
  • 20
  • 4
    So just to clarify part 2 of the NotNull part, really it should say "should not", not " cannot" since it can't bed enforced? Or if it can be enforced at runtime, how would you go about that? – DavidR Dec 04 '15 at 20:57
  • 2
    Yes, its a "should not"... the method implementation should enforce the contract. – Lucas Oliveira Dec 07 '15 at 15:35
  • 2
    Alternatively, in Java 8, `Optional` could be used in place of `@Null` in return values, and method overloading in place of `@Null` in parameter lists: http://dolszewski.com/java/java-8-optional-use-cases/ – Chad K Jul 18 '17 at 21:56
  • 18
    I believe the confusion comes from the java doc of the NotNull annotation: `* The annotated element must not be {@code null}. * Accepts any type.` and I think **must** word should be replaced with **should** but again it depends of how you read it. Definitely some more clarifications would be good to have – Julian Jul 19 '17 at 22:58
  • @Julian I think *must* is the right term because it is a rule, not a recommendation. If you use the annotation where you *should not* pass `null` but it would be allowed, you are using the annotation wrong. The term does not imply that it is validated. However, a hint that it is not validated wouldn't hurt. If you want to add automatic validation, you can use some external tools. For example, the *IntelliJ* IDE has builtin support to inject null-checks. – JojOatXGME Jun 13 '20 at 11:08
  • 1
    @Julian 'must not' seems correct. It must not contain null otherwise the call will likely not work or produce faulty outcomes. 'cannot' would indicate that it will be rejected, which would be correct to use if the annotation would enforce it, which it doesn't. So the terminology used seems correct. – Frank Hopkins Jul 30 '20 at 07:15
42

As mentioned above @NotNull does nothing on its own. A good way of using @NotNull would be using it with Objects.requireNonNull

public class Foo {
    private final Bar bar;

    public Foo(@NotNull Bar bar) {
        this.bar = Objects.requireNonNull(bar, "bar must not be null");
    }
}
Paulo Merson
  • 13,270
  • 8
  • 79
  • 72
Bollywood
  • 455
  • 4
  • 9
28

To make @NonNull active you need Lombok:

https://projectlombok.org/features/NonNull

import lombok.NonNull;

Follow: Which @NotNull Java annotation should I use?

Rany Albeg Wein
  • 3,304
  • 3
  • 16
  • 26
gavenkoa
  • 45,285
  • 19
  • 251
  • 303
7

If you are using Spring, you can force validation by annotating the class with @Validated:

import org.springframework.validation.annotation.Validated;

More info available here: Javax @NotNull annotation usage

You could also use @NonNull from projectlombok (lombok.NonNull)

sisanared
  • 4,175
  • 2
  • 27
  • 42
6

SO @NotNull just is a tag...If you want to validate it, then you must use something like hibernate validator jsr 303

ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
 Set<ConstraintViolation<List<Searching>> violations = validator.validate(searchingList);
Naruto
  • 4,221
  • 1
  • 21
  • 32
  • 1
    Where do I put this, in the beginning of the method? – DavidR Dec 04 '15 at 17:54
  • yes..at the beginning of the method...this is just one of the validation implementations ,there might be others also... – Naruto Dec 04 '15 at 17:58
  • Ok. But this significance of what that code does will not change whether or not I have the @NotNull annotation in the param argument? – DavidR Dec 04 '15 at 18:00
  • 1
    Now you have all the Violation in the set, check its size, if its greater then zero,then return from method. – Naruto Dec 04 '15 at 18:01
4

I do this to create my own validation annotation and validator:

ValidCardType.java(annotation to put on methods/fields)

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

And, the validator to trigger the check: 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));
    }
}

You can do something very similar to check @NotNull.

WesternGun
  • 11,303
  • 6
  • 88
  • 157
1

To test your method validation in a test, you have to wrap it a proxy in the @Before method.

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

With MethodValidationProxyFactory as :

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

}

And then, add your test :

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

}
Julien Feniou
  • 964
  • 8
  • 9
-3
I resolved it with

@JsonSetter(nulls = Nulls.AS_EMPTY)
@NotBlank
public String myString;

Request Json:
{
  myString=null
}
 Response:
 error must not be blank
sartysam
  • 23
  • 5