Usando a anotação NotNull no argumento do método


156

Comecei a usar a @NotNullanotação com Java 8 e a obter resultados inesperados.

Eu tenho um método como este:

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

Eu escrevi um teste JUnit passando o valor nulo para o argumento searchList. Eu esperava que algum tipo de erro acontecesse, mas ocorreu como se a anotação não estivesse lá. Esse comportamento é esperado? Pelo que entendi, isso foi para permitir que você pule a gravação do código de verificação nulo padrão.

Uma explicação do que exatamente o @NotNull deveria fazer seria muito apreciada.


29
@NotNullé apenas uma anotação. As anotações não fazem nada por si mesmas. Eles precisam de um processador de anotação no momento da compilação ou de algo que o processe no tempo de execução.
— Sotirios Delimanolis

Você está executando o código dentro de um servidor de aplicativos (por exemplo, usando o Arquillian )?
— precisa saber é o seguinte

1
@SotiriosDelimanolis - Então, qual é o objetivo, apenas um aviso para quem chama o método para não passar um valor nulo? Nesse caso, você ainda precisa do código de validação de ponteiro nulo.
— precisa

1
veja o validador de hibernação
— arisalexis

@ jabu.10245 - Não está usando nenhum servidor de aplicativos.
— DavidR

Respostas:


183

@Nullablee @NotNullnão fazem nada por conta própria. Eles devem atuar como ferramentas de documentação.

A @Nullableanotação lembra você sobre a necessidade de introduzir uma verificação NPE quando:

  1. Chamando métodos que podem retornar nulos.
  2. Desreferenciando variáveis ​​(campos, variáveis ​​locais, parâmetros) que podem ser nulos.

A @NotNullanotação é, na verdade, um contrato explícito que declara o seguinte:

  1. Um método não deve retornar nulo.
  2. Uma variável (como campos, variáveis locais e parâmetros) não pode não deve ter valor nulo.

Por exemplo, em vez de escrever:

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

Você pode usar:

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

Além disso, @NotNullgeralmente é verificado pelos ConstraintValidators (por exemplo, na primavera e no hibernar).

A @NotNullanotação não faz nenhuma validação por si só porque a definição da anotação não fornece nenhuma ConstraintValidatorreferência de tipo.

Para mais informações, consulte:

  1. Validação de Bean
  2. NotNull.java
  3. Constraint.java
  4. ConstraintValidator.java

3
Então, apenas para esclarecer a parte 2 da parte NotNull, realmente deveria dizer "não deveria", não "não pode", pois não pode ser imposta? Ou se ele pode ser aplicado em tempo de execução, como você faria isso?
— precisa

Sim, é um "não deveria" ... a implementação do método deve fazer cumprir o contrato.
— justAnotherUser ...

1
Alternativamente, em Java 8, Optionalpode ser usado no lugar de @Nullem valores de retorno, e método de sobrecarga no lugar de @Nullem listas de parâmetros: dolszewski.com/java/java-8-optional-use-cases
— Chad K

13
Acredito que a confusão vem do java doc da anotação NotNull: * The annotated element must not be {@code null}. * Accepts any type.e acho que a palavra deve ser substituída por should, mas novamente depende de como você a lê. Definitivamente, seria bom ter mais esclarecimentos #
— Julian

@ Julian Eu acho que deve ser o termo certo, porque é uma regra, não uma recomendação. Se você usar a anotação em que não deve passar, nullmas seria permitido, está usando a anotação incorreta. O termo não implica que seja validado. No entanto, uma dica de que não é validada não faria mal. Se você deseja adicionar a validação automática, pode usar algumas ferramentas externas. Por exemplo, o IntelliJ IDE possui suporte interno para injetar verificações nulas.
— JojOatXGME

27

Como mencionado acima @NotNull, nada faz por si só. Uma boa maneira de usar @NotNullseria usá-lo comObjects.requireNonNull

public class Foo {
    private final Bar bar;

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

6
Apenas uma dica. Você também pode escrever tais atribuições com uma linha:this.bar = Objects.requireNonNull(bar, "bar must not be null");
— lolung

Obrigado pela dica @lolung - Agora atualizei o código acima, com base no seu comentário.
— Bollywood


6

SO @NotNull é apenas uma tag ... Se você deseja validá-lo, deve usar algo como o hibernate validator jsr 303

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

Onde eu coloco isso, no começo do método?
— precisa

yes..at o início do método ... esta é apenas uma das implementações de validação, pode haver outros também ...
— Naruto

Está bem. Mas esse significado do que esse código não muda se eu tenho ou não a anotação @NotNull no argumento param?
— DavidR

Agora você tem toda a violação no conjunto, verifique seu tamanho, se for maior que zero, depois retorne do método
— Naruto


2

Eu faço isso para criar minha própria anotação e validador de validação:

ValidCardType.java(anotação para colocar em métodos / campos)

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

E, o validador para acionar a verificação 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));
    }
}

Você pode fazer algo muito semelhante para verificar @NotNull.


0

Para testar sua validação de método em um teste, você deve agrupar um proxy no método @Before.

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

Com MethodValidationProxyFactory como:

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

}

E então, adicione seu teste:

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

}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.