我正在try 用Java构建REST API,并测试了可选/非必需的@RequestParam,但我收到了以下警告:

Null type safety: The expression of type 'String' needs unchecked conversion to conform to '@Nonnull String'Java(16778128)

在下面的代码中:

@RestController
@RequestMapping(path = "/api/v0.1/users")
public class UserController {

    @Autowired
    UserService userService;

    @PostMapping()
    public ResponseEntity<String> postUser(
        UserCreationDTO userData,
        @RequestParam(required = false) String ignoreRequest
    ) {
        try{
            if (!Strings.isNullOrEmpty(ignoreRequest)) { // <---------------------- HERE BE THE PROBLEM
                return new ResponseEntity<String>(ignoreRequest, HttpStatus.OK);
            }
            return userService.createUser(userData);
        }catch (Exception exception) {
            exception.printStackTrace();
        }

        return BackendUtils.getResponseEntity(
            BackendConstants.SOMETHING_WRONG,
            HttpStatus.INTERNAL_SERVER_ERROR
        );

    }

谷歌搜索只会抛出人们使用原始列表的问题,这些问题假设元素将是非空的.在这里,我实际上是在try 判断一个变量的"空性",该变量在设计上可能为空.我对此的理解是,Strings.isNullOrEmpty希望接收保证不为空的输入字符串?我可能没有正确理解这一点,因为这将完全违背方法的目的……

推荐答案

基于注释的空性判断的问题在于,在这两种情况下,它都会陷入麻烦:

  • 你被迫转到@NonNull everywhere.因此,这些系统通过假设一切都是用@Nullable标注的非空unless来解决问题,可能不会走得太远(因为这与默认的Java完全不兼容--这是一个你不应该做的假设),并依赖于要应用于类甚至整个包或模块的@NonNullByDefault标注.

  • 对于代码库使用的许多方法来说,无效性信息是未知的.很简单,这总是正确的,因为根据定义,java.*基本上是"代码库使用的",而is not annotated是"代码库使用的".无效批注系统使用"外部批注"的概念来解决这个问题:"补丁"的列表,指示external代码中哪个方法签名的哪些部分(即依赖项;您只有类文件/您仅有的源文件可供参考,而不是您自己编译这些文件)should have had某个批注.

以此为背景,我们现在可以开始讨论问题了:

Your nullity annotation linter is broken.

具体地说,它正试图避免"灾区"(使用无效性未知的方法);简单地假设‘嘿,它的基本Java我们不知道,所以为了安全起见,让我们假设@Nullable’是incredibly个讨厌的东西,所以它也不会这么做.

在某个地方,这些激励措施变成了珍珠形,并导致林特工具猜测,对于这种方法:

package com.google.common.base;

public class Strings {
  ...

  public static boolean isNullOrEmpty(@NoNullityAnnotation String string) {
    ...
  }
}

...将被视为该参数被注释为@NonNull.从方法is the incorrect guess的名称来看,这是微不足道的.尽管如此,空性判断器会处理这个错误,并因此告诉您您搞砸了:如果不首先确保字符串不为空,就不能将字符串传递给这个isNullOrEmpty方法.that是你收到的警告.

这太疯狂了,因为guava does have null annotations!.

所以,你的林特工具甚至都没有把它们捡起来.

(胆小的)对您的空注释Linter.的辩护

问题是,通过注释的无效性比几乎任何人意识到的都要复杂得多.例如,就像泛型一样,"一列数字"的概念实际上有4种味道:

  • List<Number>
  • List<? extends Number>
  • List<? super Number>
  • List /* raw / legacy mode */

泛型中的无效性也有四种风格:

  • 的列表绝对不能为空字符串..get(0) does not需要空判断..add(x)不能被调用,除非x是@NonNull.
  • 绝对可以是空字符串的列表..get(0) does需要空校验; .add(x)可以用任何东西调用,甚至是未空校验的东西.
  • 名单上的我其实不知道.这很好-只要接受List<@WhoKnowsWhatNullity String>的代码遵守both限制:它为空判断.get(0)的结果and它从不调用.add(x)其中x不为非空,则它does not matter
  • 遗留/原始模式:代码做它所做的事情,它是不安全的,编译器不会阻止它的发生;类似于泛型中的原始模式.

然而,几乎没有空注释框架有这一点;很少有人知道这一点,而有些框架(如JSpecify,其作者知道这一点)是最后一次我在围栏上判断这种复杂性是否值得.这只是一堆并发症之一.

因此,实际上有15个无效性注释系统,其中大多数在语义上不匹配,这反过来意味着无效性判断器不能只编写所有常见无效性框架的知识并与其一起运行.

再说一次,guava是最受欢迎的库之一,它确实有nullity注释,这个nullity linter判断器决定只是盲目地(错误地)猜测"当然,@ Nonlity是这里的意图"),这是一个非常非常糟糕的猜测.所以,你这里大多有蹩脚的工具,你可能想提交一些功能请求,他们在猜测方面做得更好.因为这个错误的猜测很难原谅.

把它修好

毫无疑问,您的皮棉系统有一个外部注释系统.判断是否有人定义了番石榴,并将它们添加到外部注释存储中.如果没有,那就自己做吧.然后,这个问题就会消失.如果你找不到番石榴,你要么不得不处理一大堆错误的警告,要么你不得不丢弃番石榴,或者你不得不丢弃你的无效棉.对于这种情况,‘沟番石榴’是一个简单的答案,因为isNullOrEmpty无处不在.但是,您可能在代码库中的更多地方使用了番石榴,而且您的LinterTool肯定在更多的地方误判了番石榴的无效性,而不仅仅是在isNullOrEmpty个地方.

Java相关问答推荐

@ EnableRouting注释在Kotlin项目中不工作

无法找到符号错误—Java—封装

如何以干净的方式访问深度嵌套的对象S属性?

Spring Boot 3.2.2中的@Inject和@Resource Remove

如何创建同一类的另一个对象,该对象位于变量中?

这是什么Java构造`(InputStream Is)->;()->;{}`

在Java中,在单个逻辑行中连接列表和单个元素的正确方法是什么?

JavaFX如何在MeshView中修复多个立方体?

在学习Spring时,通过构造函数参数0表达了不满意的依赖关系

如何在JavaFX循环中完美地制作一个AudioClip/MediaPlayer?

允许同时执行两个方法,但不能同时执行这两个方法

Java在操作多个属性和锁定锁对象时使用同步和易失性

使IntelliJ在导入时优先 Select 一个类或将另一个标记为错误

升级版本后出现非法访问错误

将@Transactional添加到Spring框架中链下的每个方法会产生什么效果?

java 11上出现DateTimeParseException,但java 8上没有

将Optionals/null安全添加到嵌套的flatMap/流

如何在 WebSphere Application Server 内的托管线程上运行 BatchEE 作业(job)?

Vaadin Flow:设置密码显示按钮属性

Xml Reader 将 BMP 外部的字符解析为代理项对,这会导致无效的 xml