我有一个基本上像这样的程序:
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;
}
}
正如您所看到的,情况2仅在先前存在状态1并且在初始化数组后切换到状态2时才会发生。但是Eclipse和Java编译器看不到这一点,因为对于他们来说,逻辑看起来非常复杂。所以Eclipse抱怨:
本地变量ststNThings可能尚未初始化。”
如果我将“ boolean[] stuffNThings;
”更改为“ boolean[] stuffNThings=null;
”,它将切换到以下错误消息:
可能的空指针访问:在该位置,变量ststNThings可能为空。
我也不能在顶部初始化它,因为数组的大小仅在状态1的最终循环之后确定。
Java认为那里的数组可以为null,但我知道不能。有什么办法可以告诉Java吗?还是我肯定被迫对它进行无用的null
检查?加上它会使代码更难理解,因为看起来好像在某些情况下该值实际上并未设置为true
。
答案 0 :(得分:5)
Java认为那里的数组可以为null,但我知道不能。
严格来说,Java认为该变量可以未初始化。如果未明确初始化,则该值不应是 observable 。
(该变量是默默地初始化为null
还是保留为 indeterminate 状态,这是实现细节。要点是,该语言指出,不应让您看到值。)
但是无论如何,解决方案是将其初始化为null
。这是多余的,但是没有办法告诉Java“只要相信我,它就会被初始化”。
在获取“潜在的空指针访问”消息的版本中:
您可以使用@SuppressWarnings
注释禁止显示特定警告:
@SuppressWarnings("null")
。 对于Android,请使用@SuppressWarnings("ConstantConditions")
。
不幸的是,警告标签尚未完全标准化。但是,编译器应该静默忽略@SuppressWarnings
来表示它无法识别的警告标记。
您也许可以重组代码。
在您的示例中,代码使用了switch drop through。人们很少这样做,因为它导致难以理解的代码。因此,您会发现涉及直接插入的极端情况示例,使编译器得到NPE警告有点错误,我并不感到惊讶。
无论哪种方式,您都可以通过重组代码轻松地避免进行直接插入的需要。将case 2:
情况下的代码复制到case 1:
情况下。固定。继续前进。
请注意,“可能未初始化”错误不是Java编译器“愚蠢”。 JLS的一整章都是关于“确定分配”的规则等等。不允许Java编译器精明,因为这意味着相同的Java代码合法还是不合法,具体取决于编译器的实现。这将不利于代码的可移植性。
这里实际上是一种语言设计折衷方案。该语言使您无法使用(确实)未初始化的变量。但是要做到这一点,“笨拙”的编译器有时必须使您停止使用(聪明的程序员)知道将要初始化的变量……因为规则认为应该如此。
(替代方法更糟:要么在编译时检查未初始化的变量,否则在无法预测的地方导致硬崩溃,要么检查对于不同的编译器而言是不同的。)
答案 1 :(得分:0)
一个独特的非答案:当代码“太”复杂以至于IDE / java编译器没有“看到”它时,这很好地表明您的代码反正太复杂了 。至少对我来说,你说的不清楚。我不得不反复阅读和说服自己,以使自己确信问题中的陈述是正确的。
您在for的开关中有一个if。干净的代码和“单层抽象”将告诉您:这不是一个好的起点。
查看您的代码。您所拥有的是伪装的状态机。问问自己,是否有必要进行大规模重构,例如将其转变为某种 explicit 状态机。
另一个不太麻烦的想法:使用List而不是数组。然后,您可以简单地创建一个空列表,并根据需要添加元素。
答案 2 :(得分:0)
在不考虑Eclipse抱怨的情况下尝试执行代码后,我注意到它确实可以毫无问题地运行。显然,尽管这并不重要,但这只是将警告设置为“错误”级别。
有一个“配置问题严重性”按钮,因此我将“潜在的空指针访问”的严重性设置为“警告”(并相应地调整了其他一些级别)。现在Eclipse只是将其标记为警告,并且无需抱怨即可执行代码。
答案 3 :(得分:0)
更容易理解的是:
boolean[] stuffNThings;
boolean initialized = false;
for (String string: list) {
if (!initialized) {
if (!/*condition*/) {
stuffNThings = new boolean[/*size*/];
initailized = true;
}
}
if (initialized) {
// bar
stuffNThings[0] = true;
}
}
两个循环,一个循环用于初始化,另一个循环用于玩游戏。
进行流量分析更容易(与具有穿透功能的开关相比)。
此外,也可以使用boolean[]
代替BitSet
(因为它的大小不固定为数组)。
BitSet stuffNThings = new BitSet(/*max size*/);