JSR-308:Nonnull,ParameterAreNonnullByDefault的澄清以及Eclipse Kepler检测它的方式

时间:2014-02-15 16:23:38

标签: java eclipse

我决定编辑我的问题,看了一年之后,我改变了我使用null的方式:

  • 我不使用Eclipse内置的null检查,因为我发现它相当原始(并且可能有点难以理解)
  • 我使用@Nullable来告诉值可以为null。毕竟,空值应该小于非空值。
  • 我正在使用Java 8,我倾向于使用Optional,因此允许以下内容:Optional.ofNullable(value).orElseGet(() -> 1);。它没有超过Groovy的?:?.运算符,但是可选提供了一些不错的工具,例如mapfilter,等等。

至于我的代码:

  • 构造函数使用Objects.requireNonNull检查空值,如下所示:

    public Foobar(String a){   this.a = Objects.requireNonNull(a,“a”); }

  • 每当我在项目或Preconditions.checkNotNull中使用时,
  • 方法都会使用来自Guava框架的Objects.requireNonNull检查空值:

    public void foobar(String a){   Preconditions.checkNotNull(a,“a”); }

使用其中一个取决于我是否重用该值。

我不是每次都检查方法参数,而是主要使用public方法。我们的想法不是替换比我能更有效地抛出NullPointerException的默认运行时检查。


我目前在所有参数,字段,方法结果(返回)上使用@Nonnull@Nullable注释,但我想知道什么才是最好的:

  • 默认情况下,如何判断我的字段和方法结果是否为空? (@ParameterAreNonnullByDefault对他们不起作用)。我想要一种可移植的方式(我read here我可以创建自己的注释,具有特定的名称,并且适用于findbugs)
  • 如果我使用com.foobar注释包@ParameterAreNonnullByDefault,它是否也适用于com.foobar.example
  • 我应该在@Nonnull注释时检查每个参数(我目前正在检查构造函数参数)吗?

此外,自Eclipse 3.8以来,存在基于注释的空检查。但我对一些“简单”案件有疑问:

@ParameterAreNonnullByDefault
class Foobar<E extends Throwable> {
  @Nullable private Constructor<E> one;
  @Nullable private Constructor<E> two;

  public Foobar(Constructor<E> one, @Nullable Constructor<E> two) {
    this.one = Objects.requireNonNull(one, "one");
    this.two = two;
  }

  // don't care about exceptions.
  public E getInstance(String msg, Throwable t) { 
    if (null == two) {
      return (E)one.newInstance(msg).initCause(t);
    } 
    return two.newInstance(msg, t);
  }
}

为什么告诉我两个在那个位置可以为空,为什么他警告我有可能对两个人进行无效访问?

3 个答案:

答案 0 :(得分:1)

twogetInstance变量的警告而言,空分析并不足以证明该字段不能为空。您可以使用局部变量解决此问题:

public E getInstance(String msg, Throwable t) { 
  final Constructor<E> localTwo = two;
  if (null == localTwo) {
    return (E)one.newInstance(msg).initCause(t);
  }
  return localTwo.newInstance(msg, t);
}

Enable syntactic null analysis for fields中有Preferences > Java > Compiler > Errors/Warnings > Null analysis设置,允许使用以下代码:

if (two != null) {
  return two.newInstance(msg, t);
}

没有警告。

答案 1 :(得分:1)

以下是使用Eclipse实现所有请求的方法:

默认情况下,字段和方法返回非空

自Eclipse Kepler以来,@NonNullByDefault注释会影响所有这些位置。从Luna开始,使用Java 8并输入注释,效果为can be further fine tuned

当您询问可移植解决方案时:在CI构建中使用Eclipse编译器并不困难,请参阅JDT FAQ,甚至可以告诉IntelliJ使用Eclipse编译器。

每包默认值

Java没有子包的概念。如果包名称共享一个公共前缀,则不会产生语义含义。 因此,遗憾的是,每个包装都必须单独注释。

准备好将默认值应用于所有软件包后,如果错过了软件包的默认设置,可以告诉Eclipse警告/甚至引发错误。该选项被称为:“在包装上缺少'@NonNullByDefault'注释”。

运行时检查

在理想的世界中,编译器将完全检查所有@NonNull个参数,您不需要任何运行时检查。这是一个关于添加这些检查需要多少安全性的问题,但是如果你这样做,我认为没有太多理由将构造函数参数与方法参数区别对待。

也许,如果您有final @NonNull个字段,构造函数负有特殊责任并需要更多检查,但这基本上取决于您的代码风格和安全要求。

Eclipse中的意外警告

Eclipse仅对局部变量执行全流分析。它是以这种方式精心设计的,以保护您免受risks of side-effects, aliasing and concurrency的影响,尽管事先进行了空检查,但每个都可能导致NPE。对于字段的所谓“语法分析”已被引入作为(有限的)折衷,但真正的解决方案必须是,将值提取到局部变量中,在那里可以对其进行全面分析。 Eclipse甚至可以快速修复此更改。

答案 2 :(得分:0)

我会使用外部工具Checker Framework Nullness Checker来回答您的问题。它有一个Eclipse plug-in,它读取Eclipse和FindBugs使用的注释。它的分析优于内置的Eclipse nullness分析,但它的IDE集成并不像Eclipse那样好。两者都是值得的工具,你可以决定哪一个最适合你。

  

默认情况下,如何判断我的字段和方法结果是否为空?

那是the default。你不必做任何事情。

  

如果我使用@ParameterAreNonnullByDefault注释com.foobar包,它是否也适用于com.foobar.example?

您根本不必更改默认设置(尽管您可以在某些或所有代码位置使用@Nullable作为默认设置)。

  

@Nonnull注释时,我应该检查每个参数(我目前正在检查构造函数参数)吗?

如果您担心归零度分析不合理,您只需要编写运行时检查 - 也就是说,即使您的代码可以抛出空指针异常,或者如果您没有运行分析,分析也不会出现错误在你的整个计划。 Eclipse和FindBugs的零点分析都不合理。 Checker框架设计合理。当然,任何工具都可能包含错误。

  

此外,自Eclipse 3.8以来,存在基于注释的空检查。但我对一些&#34;简单&#34;有问题。情况下:

Checker Framework精确处理这个简单的案例 - 完全按照您的意愿处理。

更具体地说,Checker Framework在检查单线程代码时会这样做。

如果您的代码是多线程的,那么您可以提供-AconcurrentSemantics命令行选项。在这种情况下,Checker Framework将发出警告,因为另一个线程可能会在检查和使用之间重置two。如果您的代码是多线程的并且其他线程同时修改字段,那么您可能想要使用锁。如另一个答案所示,将值复制到局部变量是一个坏主意。它不仅会降低代码的可读性,而且并不一定能按照您的意愿行事;例如,另一个线程可能违反表示不变量并导致代码以其他方式崩溃。