为什么我的项目中不存在@NotNull 或@Nullable?

Why doesn't @NotNull or @Nullable exist in my project?

我正在使用 IntelliJ 进行编程,我刚刚在做一个 Java 项目,我想制作一个注释为 @NotNull 的参数。但是 IntelliJ 说它不存在。我已经检查了所有 java 文件,但它不在我的项目中。我很困惑,我没有使用 maven 或 gradle,只是默认的 Java 项目。我不知道发生了什么。

这是一个例子:

package com.company;


    public class Main {
         public static void main(String[] args){
                testF(null);
         }
         public static void testF (@NotNull Integer... numbers){
            for(Integer integer: numbers){
                   System.out.println(integer);
          }
     }
}

说“无法解析符号 NotNul : 10”时出错(再次@在前面)

Java 本身不附带这些注释。

相反,大约有 10 个相互不兼容的想法,其中大部分的工作方式完全不同,并且对 NonNull 的含义、放置位置以及工作方式有不同的含义。

哎呀。这是非常不幸的,因为使用注释来添加此信息的基本思想远远优于 Optional 等,因为它完全向后兼容并且不会将现有代码降级为过时的低迷,这与 Optional 不同。

所以,找到一个你喜欢的,并将它包含在你的项目中,就像你包含任何第三方依赖项一样 - 通常通过将它包含在你的 Maven/Gradle/Ant+ivy/etc(你的构建文件的列表依赖项)。

Intellij 对 NonNull 和 Nullable 有自己的看法。这可能是最方便的。它关于这些注释的含义的想法是劣等的1。 Checker Framework 是最好的,eclipse 远远排在第二,其他一切(包括 intellij 的)都排在第三位。最好的方法是在 Checker Framework 中找到,或者几乎同样好,eclipse 对这些注释的看法。但是,我怀疑 intellij 的 null 检查系统是否能够完全理解其更高级的添加,例如 @PolyNull,因此这种额外的表达能力将大部分被浪费掉。作为奖励,intellij 附带了一堆关于主要库上正确的无效注释的数据。

最后一个很重要:最常用的 java 库,包括 java.* 本身, 没有这些注释 ,并且使用 half-空注释代码绝对是一个非常令人沮丧的练习;这样做的成本远远超过收益。唯一真正的解决方案是 'fix' 您使用的库具有正确的无效信息,但这是一项大量的工作。幸运的是,intellij 已经为你做了很多。

我希望(eclipse 这样做),quickfix(在 mac 上为 CMD+1,在非 mac 上为 CTRL+1,至少,如果我对默认键盘快捷键的记忆对我有用的话,开箱即用)包括“自动将 eclipse 的无效注释添加到类路径”(或者在您的情况下,当然是 intellij)。如果不知何故没有出现,This page from the intellij docs 准确解释如何将包含它们的无效注释的 org.jetbrains.annotations 库添加到您的项目中。事实上,这些文档表明,quickfix 菜单确实为您提供了自动添加此库的选项,作为您在源代码中的 @NonNull 节点上遇到的错误的解决方案。

[1] 大多数采用无效注释通过仅允许在字段、方法(暗示:它是什么 returns)和参数上进行注释来极大地限制自己。但是,可以有一个 definitely-not-null List of could-be-null Map 实例,其中 Map definitely not null String to could be null Integer: @NonNull List<@Nullable Map<@NonNull String, @Nullable Integer>>。注释系统能够让你这样写,但前提是你的注释是专门为 TYPE_USE 设置的。检查器框架和 Eclipse 的无效注释就是这样工作的;大多数其他人没有,因此表现力较差。 CheckerFramework 更进一步,让您可以编写 'either nullity is fine' 的概念。就像泛型有 3 种形式(List<Integer>List<? super Integer>List< extends Integer>,一旦涉及到泛型,2 个 nullities(要么从不为 null,要么绝对允许 null)就不再足够了,你需要更多的空值。检查器框架有 @PolyNull 会让你 link 空值:例如,你可以在 checkerframework 中编写这个方法,但你不可能用 intellij 或 eclipse 正确地写它:

public void duplicateFirstMatch(List<T> elems, Predicate<T> matcher);

想法是:此方法针对列表中的每个元素运行匹配器,并且在匹配时,将该元素添加到列表的末尾。如果 T 被认为是 '@NonNull'(假设没有空值,则此代码永远不能添加空值,因此它不能违反其元素的非空性),此方法可以工作,但它如果 T@Nullable,当然也可以工作,只要匹配器也是 @Nullable T:现在这段代码可能会将 null 添加到列表中,但没关系。

因此 T 既不是 Nullable 也不是 NonNull,但是签名中提到的 T 确实需要匹配它们的空值。 @PolyNull 解决了这个问题。