【问题标题】:Using NotNull Annotation in method argument在方法参数中使用 NotNull 注解
【发布时间】:2016-03-09 17:20:07
【问题描述】:

我刚开始在 Java 8 中使用 @NotNull 注释并得到了一些意想不到的结果。

我有这样的方法:

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

我编写了一个 JUnit 测试,为参数 searchList 传递了空值。我期待会发生某种类型的错误,但它就像注释不存在一样。这是预期的行为吗?据我了解,这是为了让您跳过编写样板空检查代码。

非常感谢您解释 @NotNull 究竟应该做什么。

【问题讨论】:

  • @NotNull 只是一个注释。注释本身没有任何作用。他们在编译时需要一个注解处理器,或者在运行时处理它的东西。
  • 您是否在应用服务器中运行代码(例如使用Arquillian)?
  • @SotiriosDelimanolis - 那么重点是什么,只是警告任何调用该方法的人不要传递空值?在这种情况下,您仍然需要空指针验证代码。
  • 查看休眠验证器
  • @jabu.10245 - 不使用任何应用服务器。

标签: java


【解决方案1】:

要激活@NonNull,您需要 Lombok:

https://projectlombok.org/features/NonNull

import lombok.NonNull;

关注:Which @NotNull Java annotation should I use?

【讨论】:

    【解决方案2】:

    如果您使用的是 Spring,您可以通过使用 @Validated 注释类来强制验证:

    import org.springframework.validation.annotation.Validated;
    

    更多信息在这里: Javax @NotNull annotation usage

    你也可以使用projectlombok中的@NonNull (lombok.NonNull)

    【讨论】:

      【解决方案3】:
      I resolved it with
      
      @JsonSetter(nulls = Nulls.AS_EMPTY)
      @NotBlank
      public String myString;
      
      Request Json:
      {
        myString=null
      }
       Response:
       error must not be blank
      

      【讨论】:

      • 您能否提供更多解释,说明这将如何帮助解决提问者的问题?
      【解决方案4】:

      @Nullable@NotNull 自己什么都不做。它们应该充当文档工具。

      @Nullable 注解提醒您在以下情况下引入 NPE 检查的必要性:

      1. 调用可以返回 null 的方法。
      2. 取消引用可以为 null 的变量(字段、局部变量、参数)。

      @NotNull 注解实际上是一个明确的契约,声明如下:

      1. 方法不应返回 null。
      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 检查(例如在 spring 和 hibernate 中)。

      @NotNull 注释本身不进行任何验证,因为 annotation definition 不提供任何 ConstraintValidator 类型引用。

      更多信息见:

      1. Bean validation
      2. NotNull.java
      3. Constraint.java
      4. ConstraintValidator.java

      【讨论】:

      • 所以只是为了澄清 NotNull 部分的第 2 部分,真的应该说“不应该”,而不是“不能”,因为它不能强制执行?或者如果它可以在运行时强制执行,你会怎么做?
      • 是的,它是“不应该”……方法实现应该强制执行合同。
      • 另外,在 Java 8 中,Optional 可以用来代替返回值中的 @Null,并且方法重载可以代替参数列表中的 @Nulldolszewski.com/java/java-8-optional-use-cases
      • 我相信混淆来自 NotNull 注释的 java 文档:* The annotated element must not be {@code null}. * Accepts any type. 我认为 must 单词应该替换为 should 但是再次,这取决于你如何阅读它。肯定有更多的澄清会很好
      • @Julian 我认为 must 是正确的术语,因为它是规则,而不是建议。如果您在 不应 传递 null 但允许的地方使用注释,则说明您使用了错误的注释。该术语并不意味着它已被验证。但是,暗示它未经验证不会受到伤害。如果要添加自动验证,可以使用一些外部工具。例如,IntelliJ IDE 内置支持注入空检查。
      【解决方案5】:

      我这样做是为了创建自己的验证注解和验证器:

      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

      【讨论】:

        【解决方案6】:

        要在测试中测试您的方法验证,您必须在 @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));
        
        }
        

        【讨论】:

          【解决方案7】:

          如上所述,@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");
              }
          }
          

          【讨论】:

          • 只是小费。你也可以用一行写这样的作业:this.bar = Objects.requireNonNull(bar, "bar must not be null");
          • 感谢@lolung 的提示 - 我现在已经根据您的评论更新了上面的代码。
          • 如果 bar 字段使用 NotNull 注释,对 Objects.requireNonNull 的调用仍会生成 à 警告,因为 requireNonNull 方法不会返回 NotNull 引用。
          【解决方案8】:

          所以 @NotNull 只是一个标签...如果你想验证它,那么你必须使用类似 hibernate validator jsr 303 的东西

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

          【讨论】:

          • 我应该把这个放在哪里,在方法的开头?
          • 是的..在方法的开头......这只是验证实现之一,可能还有其他......
          • 好的。但是,无论我在 param 参数中是否有 @NotNull 注释,该代码所做的事情的重要性都不会改变?
          • 现在你有了集合中的所有 Violation,检查它的大小,如果它大于零,则从方法返回。
          猜你喜欢
          • 2014-08-21
          • 2017-09-26
          • 1970-01-01
          • 1970-01-01
          • 2021-06-04
          • 2016-08-22
          • 1970-01-01
          • 2020-11-25
          • 2020-01-08
          相关资源
          最近更新 更多