如何告诉Java变量不能为null?

Fab*_*ing 1 java null initialization

我有一个程序,基本上像这样:

boolean[] stuffNThings;
int state=1;
for(String string:list){
   switch(state){
      case 1:
         if(/*condition*/){
            // foo
            break;
         }else{
            stuffNThings=new boolean[/*size*/];
            state=2;
         }
      // intentional fallthrough
      case 2:
         // bar
         stuffNThings[0]=true;
   }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,情况2仅在先前存在状态1且在初始化数组后切换到状态2时才会发生。但是Eclipse和Java编译器看不到这一点,因为对于他们来说,逻辑看起来非常复杂。因此,Eclipse抱怨:

本地变量ststNThings可能尚未初始化。”

如果我将“ boolean[] stuffNThings;” 更改为“ boolean[] stuffNThings=null;”,则会切换到以下错误消息:

潜在的空指针访问:在此位置,变量ststNThings可能为空。

我也无法在顶部进行初始化,因为数组的大小仅在状态1的最终循环之后确定。

Java认为那里的数组可以为null,但我知道不能。有什么办法可以告诉Java吗?还是我肯定被迫对其进行无用的null检查?添加该代码会使代码更难于理解,因为看起来在某些情况下该值实际上并未设置为true

Ste*_*n C 5

Java认为那里的数组可以为null,但我知道不能。

严格来说,Java认为该变量可以未初始化。如果未明确初始化,则该值不应是可观察到的

(该变量是默默地初始化为不确定状态null还是保留为不确定状态,这是实现的详细信息。重点是,该语言表示不应允许您看到该值。)

但是无论如何,解决方案是将其初始化为null。这是多余的,但是没有办法告诉Java“只要相信我,它将被初始化”。


在获取“可能的空指针访问”消息的变体中:

  1. 这是警告,不是错误。
  2. 您可以忽略或取消警告。(如果正确性分析错误,那么您可能会得到NPE。但这是您的选择。)
  3. 您可以使用编译器开关关闭某些或所有警告。
  4. 您可以使用@SuppressWarnings注释取消显示特定警告:

    • 对于Eclipse,请使用@SuppressWarnings("null")
    • 对于Android,请使用@SuppressWarnings("ConstantConditions")

      不幸的是,警告标签尚未完全标准化。但是,编译器应静默忽略@SuppressWarnings无法识别的警告标记。

  5. 您也许可以重组代码。

在您的示例中,代码使用了开关插入。人们很少这样做,因为它导致难以理解的代码。因此,您会发现涉及直接插入的极端情况示例,使编译器得到NPE警告有点错误,我并不感到惊讶。

无论哪种方式,您都可以通过重组代码轻松地避免进行直接插入的需要。将case 2:案例中的代码复制到案例末尾case 1:。固定。继续。


请注意,“可能未初始化”错误不是Java编译器“愚蠢”。JLS的一整章都是关于确定赋值的规则,等等。不允许Java编译器精明,因为这意味着相同的Java代码合法还是不合法,具体取决于编译器的实现。这将不利于代码的可移植性。

我们实际上在这里是语言设计的折衷方案。该语言使您无法使用(确实)未初始化的变量。但是要做到这一点,“笨拙”的编译器有时必须使您停止使用您(聪明的程序员)知道会被初始化的变量……因为规则说应该这样做。

(更糟糕的选择是:要么不对未初始化的变量进行编译时检查,否则会在不可预测的位置导致严重崩溃;或者对不同的编译器进行检查时会有所不同。)