编译器说“变量可能没有被初始化”,虽然我有一个标志变量来保证它

时间:2017-12-01 09:03:00

标签: java

这是我的代码段:

Someclass someObject;
boolean success = true;
try {
    someObject = someOperation();
} catch (Exception e) {
    success = false;
}
if (success) {
    int number = Integer.valueOf(someObject.someMethord());
}

并且在行内:

 int number = Integer.valueOf(someObject.someMethord());

Java编译器抛出错误说

  

错误:变量some​​Object可能尚未初始化。

但是,如果success等于true,那么someObject无法初始化,为什么我会收到此错误?

7 个答案:

答案 0 :(得分:51)

编译器不会分析success标志与someObject变量初始化之间的关系。

就编译器而言,如果发生异常,可能无法初始化someObject

您可以通过在catch块中将变量设置为null来解决该问题(而不是检查success变量,检查someObject != null)。

或者您可以在try块中移动int number = Integer.valueOf(someObject.someMethord());语句。

答案 1 :(得分:11)

Java语言规范(JLS)确切地定义了编译器应如何分析这样的代码。

在您的情况下,本地变量someObjectif块中使用之前不会明确分配chapter 16 of the JLS中涵盖的明确赋值定义了可以将变量视为(“初始化”)的确切规则。

分别分析tryif语句。在try后,someObject 明确分配,因为它未在catch块中分配。在if中,条件可以是truefalse。如果是true,则会收到错误,因为此时未明确分配someObject

Java编译器不允许分析此代码,并“弄清楚”success只有在分配someObject时才能为真,因为语言规则规定了必须进行的精确分析。这不是编译器不够智能的情况 - 这是Java语言标准严格的情况。

请注意,如果您使用if(false)而不是if(success),则不会收到错误,因为JLS指定false常量表达式,因此循环体永远不会执行。

在任何情况下,都不需要flag变量。将相关代码移动到try中,或者在声明中将变量设置为null并显式检查someObject != null,这些方法都更容易理解,更不容易出错。< / p>

答案 2 :(得分:9)

您可以像这样更改声明:

Someclass someObject = null;

或者您可以在try-catch中执行所有操作以确保someObject将正确初始化

try {
    Someclass someObject = someOperation();
    int number = Integer.valueOf(someObject.someMethod());
} catch (Exception e) {
    //...
}

答案 3 :(得分:3)

编译器只进行非常简单的静态分析,这是设计的。静态分析可以随着时间的推移变得更加智能,当您切换到较旧的编译器版本时,您肯定不希望代码停止编译。

另一个原因是保持编译器快速。智能分析对于优化很重要,但其成本很高。优化不是javac的工作(它们在运行时发生),因此javac不会打扰。

它甚至不会识别像

这样的琐碎案例
int f(boolean b) {
    if (b) {
        return 1;
    } else if (!b) {
        return 0;
    }
}

规则在JLS Chapter Definite Assignment

中指定

答案 4 :(得分:2)

您可以将此方案视为 -

What happens is your try blocks fails to initialize the someObject.

这意味着someOperation()方法中可能存在一些异常。将捕获异常,但someObject将不会被初始化。

您可以通过在catch块中将someObject设置为null或new SomeObject()来解决此问题。

答案 5 :(得分:2)

您在Java中描述的内容与C#非常相似(消息为CS0165 Use of unassigned local variable 'someObject')。两种语言的解决方案都是进行空值分配:

      SomeClass someObject = null;

警告将消失。正如其他答案所描述的那样,原因很简单,编译器不够智能,所以这些警告是一种权衡。

完整的Java示例:

class SomeClass
{
    public int someMethod()
    {
      return 1;
    }
}

public class JavaFiddle
{

    public static SomeClass someOperation()
    {
        SomeClass result = new SomeClass();
        return result;
    }

    public static void main(String[] args)
    {
      SomeClass someObject = null;
        boolean success = true;
        try {
            someObject = someOperation();
        } catch (Exception e) {
         success = false;
        }
        if (success) {
            int number = Integer.valueOf(someObject.someMethod());
        }
    }

}

<强> Paste this code in a JavaFiddle
(press Ctrl and left-click to open a tab with JavaFiddle)

  

&GT;编译...
  &GT;跑步......

正如您所看到的,在上面的代码中问题已经修复,因此如果您将其粘贴到JavaFiddle工具中,它将按预期运行而不会出现错误。

要激发错误,按如下方式更改代码:

SomeClass someObject; // = null;

再次运行它。您将收到消息:

  

&GT;编译...
  
  /str/JavaFiddle.java:29:错误:变量some​​Object可能尚未初始化
             int number = Integer.valueOf(someObject.someMethod());
  ^
  1错误

注意我刚刚采用了“按现状”提供的示例 - 您应该考虑有关异常处理的提示(请参阅Eric Lippert对您的问题的评论)。吞没异常而不真正处理它们通常是不好的 - 更好地处理你在代码中知道的情况并让调用者处理你可能不知道的任何异常(当然,实现一些日志记录)。

答案 6 :(得分:0)

编译器对可能的变量的理解有限,例如:

 public void test() {
    int y = 2;
    Integer x;
    if (y == 2) {
        x = 2;
    }

    System.out.println(x);// this will fail
}

但是要使final y = 2和编译器在这种情况下看到y的唯一可能值为2。