我应该同时使用@NotNull 和@NonNull 吗?
Should I use @NotNull and @NonNull together?
我正在使用 javax.validation.constraints.NotNull 休眠检查空字段。现在我正在使用 lombok 来简化构造函数代码,但我需要使用 @NonNull 注释来使该字段包含在必填字段构造函数中。我应该一起使用它们吗?
对于非空注释的作用,至少有 4 完全 种不同的含义。大多数注释仅暗示这些不同事物中的一种。因此,是的,应用多个这样的注释是有意义的。不幸的是,这些注释的实际含义相当模糊,因为根据我的经验,几乎没有开发人员考虑过它们的含义不同这一事实;事实上,我遇到的大多数开发人员都完全没有注意到无效注释非常复杂且定义模糊这一事实。
类型系统解释('cannot be!')
此解释是一种类型断言,它试图与 String x;
中的 String
一样强大:不可能 x
变量来引用一个不是字符串的对象,因为这是不可能的,自然地,检查这个是一个完全无用的想法。鉴于该声明,String z = (String) x
之类的内容实际上毫无用处,实际上是编译器警告。
这样的注解也是如此。这方面的例子有 checkerframework、eclipse 和 intellij:org.checkerframeworkchecker.nullness.qual.NonNull
、org.jetbrains.annotations.NotNull
和 eclipse 的 org.eclipse.jdt.annotation.NonNull
都主要暗示了这个意思(尽管 intellij 适用于参数和方法,而 checkerframework 和 eclipse 是type_use 注释。我告诉过你这很复杂 :P)。
这些注释,通过暗示 'cannot be',用于写入时检查。出于同样的原因,编写 String x = 5;
是一个 立即 ,当您编写该代码并且甚至在您保存文件之前,IDE 中的红色波浪下划线也是例如,编写 someMethod(map.get(key))
,其中 someMethod 的参数使用这些 NonNull
注释之一进行注释。与其说是“你不应该”,不如说是"you cannot do that; I won't even let you".
当然,javac 编译器实际上并不是这样工作的,是 IDEs 和工具试图让它看起来像那样。就像您永远不会将 String 类型的变量转换为 String 一样,您实际上不会将此类无效注释视为暗示您应该检查。不;意思是:不是。这意味着检查已经完成,您无需再次检查。
当然,这很复杂:围绕 java 编译器是否会阻止您破坏这些非空注释的隐含这一事实而跳舞……一些工具(例如 kotlinc)将注入无论如何都要进行显式空检查。有点像泛型如何导致 javac 在您从未编写过显式转换的地方注入一些类型检查,以解决泛型是编译时附加组件以保持向后兼容性的事实。这个想法是考虑这些你不应该考虑的实现细节。
数据库解释
当 hibernate 使用 class 作为模板以生成 CREATE TABLE
SQL 语句时,能够使用注释来设置约束和规则以及像。出于同样的原因,您可能希望将字段标记为:"When turning this into an SQL column, tell the DB engine to put a unique index on this",您可能希望将其标记为:"When turning this into an SQL column, tell the DB engine to put a non-null constraint on it".
这与类型系统解释有关,就像枪和祖母一样:您可以拥有代表数据库中的行但由于打破约束而无法成功保存的对象一直;例如,任何自动计数的 unid 字段通常为 0,并且不会像那样保存(数据库引擎将 0 升级为“没关系;插入时不带它并让数据库引擎的序列填充此数字”)。这些也是如此:它对 java 真的没有任何意义; SQL 引擎将负责指示 insert/update 由于约束失败而失败。当然,为了节省到数据库的往返行程,并且因为这不允许很简单,所以大多数数据库框架都会在您调用 .save()
或 .store()
的等价物时进行 nullcheck。但这只是该数据库约束的捷径。
验证解释
有时 java 中的对象代表外部事物。例如 DB 行(与之前的含义有些重叠),或者说,用户提交的 Web 表单。这样的对象应该精确地表示它所表示的实际外部事物的实际状态。疣和无效的官方文章等等。
通常你有一个框架可以让你验证这些。这就是 validation nonnull 的意思:该字段可以为 null,如果您尝试将其设置为 null,则不会发生异常(因此,它们可以)。但是,只要该字段保持为空,如果您问我该对象是否有效,答案就是:否。
lombok解释
不幸的是,lombok 有点混淆了水域.. 就像所有其他框架一样。 lombok 的 @NonNull
意味着什么取决于它出现的位置。如果在一个参数上,它意味着:Lombok,请在方法的第一行生成一个显式的 nullcheck,除非我已经自己写了。
在字段上,表示:"For the sake of @RequiredArgsConstructor
, consider this field required; nullcheck it (throwing an exception) in the generated constructor. Also, copy the nullity annotation where relevant, and therefore, if a setter is made for this field.. add that nullcheck inside."
您的具体情况
鉴于无效注释意味着完全不同的事物,因此将多个此类注释应用于代码中的同一结构可能并不疯狂。如果您希望 DB 引擎生成 SQL NON NULL
约束,如果此 class 用作模板,则 和 用于表示行的任何对象在此 table 到 立即 拒绝任何将其置于无效状态的尝试,同时添加两者是有意义的。如果您更喜欢 acceptable 对象表示无效状态,如果一般原则是构造一个空白对象,然后使用 [=97 一次设置每个字段值,则几乎总是这种情况=] – 然后不添加lombok的注解。
你肯定已经涵盖了所有的复杂性,对吧?
连一丝微光都没有。对于真正的脑筋急转弯,请考虑 checkerframework's @PolyNull,并认为这是对适当类型系统应该真正能够处理的内容的过度简化!
免责声明:我是 Project Lombok 的核心贡献者。
我正在使用 javax.validation.constraints.NotNull 休眠检查空字段。现在我正在使用 lombok 来简化构造函数代码,但我需要使用 @NonNull 注释来使该字段包含在必填字段构造函数中。我应该一起使用它们吗?
对于非空注释的作用,至少有 4 完全 种不同的含义。大多数注释仅暗示这些不同事物中的一种。因此,是的,应用多个这样的注释是有意义的。不幸的是,这些注释的实际含义相当模糊,因为根据我的经验,几乎没有开发人员考虑过它们的含义不同这一事实;事实上,我遇到的大多数开发人员都完全没有注意到无效注释非常复杂且定义模糊这一事实。
类型系统解释('cannot be!')
此解释是一种类型断言,它试图与 String x;
中的 String
一样强大:不可能 x
变量来引用一个不是字符串的对象,因为这是不可能的,自然地,检查这个是一个完全无用的想法。鉴于该声明,String z = (String) x
之类的内容实际上毫无用处,实际上是编译器警告。
这样的注解也是如此。这方面的例子有 checkerframework、eclipse 和 intellij:org.checkerframeworkchecker.nullness.qual.NonNull
、org.jetbrains.annotations.NotNull
和 eclipse 的 org.eclipse.jdt.annotation.NonNull
都主要暗示了这个意思(尽管 intellij 适用于参数和方法,而 checkerframework 和 eclipse 是type_use 注释。我告诉过你这很复杂 :P)。
这些注释,通过暗示 'cannot be',用于写入时检查。出于同样的原因,编写 String x = 5;
是一个 立即 ,当您编写该代码并且甚至在您保存文件之前,IDE 中的红色波浪下划线也是例如,编写 someMethod(map.get(key))
,其中 someMethod 的参数使用这些 NonNull
注释之一进行注释。与其说是“你不应该”,不如说是"you cannot do that; I won't even let you".
当然,javac 编译器实际上并不是这样工作的,是 IDEs 和工具试图让它看起来像那样。就像您永远不会将 String 类型的变量转换为 String 一样,您实际上不会将此类无效注释视为暗示您应该检查。不;意思是:不是。这意味着检查已经完成,您无需再次检查。
当然,这很复杂:围绕 java 编译器是否会阻止您破坏这些非空注释的隐含这一事实而跳舞……一些工具(例如 kotlinc)将注入无论如何都要进行显式空检查。有点像泛型如何导致 javac 在您从未编写过显式转换的地方注入一些类型检查,以解决泛型是编译时附加组件以保持向后兼容性的事实。这个想法是考虑这些你不应该考虑的实现细节。
数据库解释
当 hibernate 使用 class 作为模板以生成 CREATE TABLE
SQL 语句时,能够使用注释来设置约束和规则以及像。出于同样的原因,您可能希望将字段标记为:"When turning this into an SQL column, tell the DB engine to put a unique index on this",您可能希望将其标记为:"When turning this into an SQL column, tell the DB engine to put a non-null constraint on it".
这与类型系统解释有关,就像枪和祖母一样:您可以拥有代表数据库中的行但由于打破约束而无法成功保存的对象一直;例如,任何自动计数的 unid 字段通常为 0,并且不会像那样保存(数据库引擎将 0 升级为“没关系;插入时不带它并让数据库引擎的序列填充此数字”)。这些也是如此:它对 java 真的没有任何意义; SQL 引擎将负责指示 insert/update 由于约束失败而失败。当然,为了节省到数据库的往返行程,并且因为这不允许很简单,所以大多数数据库框架都会在您调用 .save()
或 .store()
的等价物时进行 nullcheck。但这只是该数据库约束的捷径。
验证解释
有时 java 中的对象代表外部事物。例如 DB 行(与之前的含义有些重叠),或者说,用户提交的 Web 表单。这样的对象应该精确地表示它所表示的实际外部事物的实际状态。疣和无效的官方文章等等。
通常你有一个框架可以让你验证这些。这就是 validation nonnull 的意思:该字段可以为 null,如果您尝试将其设置为 null,则不会发生异常(因此,它们可以)。但是,只要该字段保持为空,如果您问我该对象是否有效,答案就是:否。
lombok解释
不幸的是,lombok 有点混淆了水域.. 就像所有其他框架一样。 lombok 的 @NonNull
意味着什么取决于它出现的位置。如果在一个参数上,它意味着:Lombok,请在方法的第一行生成一个显式的 nullcheck,除非我已经自己写了。
在字段上,表示:"For the sake of @RequiredArgsConstructor
, consider this field required; nullcheck it (throwing an exception) in the generated constructor. Also, copy the nullity annotation where relevant, and therefore, if a setter is made for this field.. add that nullcheck inside."
您的具体情况
鉴于无效注释意味着完全不同的事物,因此将多个此类注释应用于代码中的同一结构可能并不疯狂。如果您希望 DB 引擎生成 SQL NON NULL
约束,如果此 class 用作模板,则 和 用于表示行的任何对象在此 table 到 立即 拒绝任何将其置于无效状态的尝试,同时添加两者是有意义的。如果您更喜欢 acceptable 对象表示无效状态,如果一般原则是构造一个空白对象,然后使用 [=97 一次设置每个字段值,则几乎总是这种情况=] – 然后不添加lombok的注解。
你肯定已经涵盖了所有的复杂性,对吧?
连一丝微光都没有。对于真正的脑筋急转弯,请考虑 checkerframework's @PolyNull,并认为这是对适当类型系统应该真正能够处理的内容的过度简化!
免责声明:我是 Project Lombok 的核心贡献者。