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.
@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.
@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:
Calling methods that can return null. Dereferencing variables (fields, local variables, parameters) that can be null.
The @NotNull
Annotation is, actually, an explicit contract declaring the following:
A method should not return null. 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 (eg. 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:
Bean validation NotNull.java Constraint.java ConstraintValidator.java
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");
}
}
To make @NonNull
active you need Lombok:
https://projectlombok.org/features/NonNull
import lombok.NonNull;
Follow: Which @NotNull Java annotation should I use?
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);
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)
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
.
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));
}
I resolved it with
@JsonSetter(nulls = Nulls.AS_EMPTY)
@NotBlank
public String myString;
Request Json:
{
myString=null
}
Response:
error must not be blank
Success story sharing
Optional
could be used in place of@Null
in return values, and method overloading in place of@Null
in parameter lists: dolszewski.com/java/java-8-optional-use-cases* 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 havenull
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.