【问题标题】:Why is x not initialized in this?为什么 x 没有在此初始化?
【发布时间】:2021-06-12 14:00:25
【问题描述】:

为什么下面的x没有初始化?

public class rough {
    public static void main(String[] args) {
        int x;
        boolean found = false;
        for (int i = 0; i < 10; i++) {
            if (Math.random() < 0.5) {
                found = true;
                x = 10;
                break;
            }

        }
        if (!found)
            x = -1;
        System.out.println(x);//x isn't initialized here
    }
}

平均而言,对于一半的迭代,for 循环内的if 将是true,因此初始化x。对于另一半,found 保持false,因此外部if 将初始化。因此,我不明白为什么编译器会生气。

作为最终的蒸馏(参见下面的连续简化),考虑

public static void main(String[] args) {
        int x;
        boolean found = false;
        if (!found)
            x = -1;

        System.out.println(x);
    }

这也给出了x不是初始化的错误。

以前的简化

更令人惊讶的是,变化 if (Math.random() &lt; 0.5)if(true) 也有同样的问题。

事实上,进一步调查,用这些替换原来的for循环

        for (int i=0;i<1;i++)
           x = 10;
 for (; !found; ) {
            x = 10;
            break;
        }

同样糟糕。只有 for(;;){... break;}for(;true;){... break;} 不会引发任何初始化。错误。

【问题讨论】:

  • 编译器并没有走那么远就能理解 x 值与找到的布尔值相关联
  • @azro thnx 快速回复..我实际上使用的是 intellij IDE...我认为它在逻辑地执行代码后显示构建错误
  • 因为你知道输入的类型,而编译器不能确定x 是否总是有一个分配给它的值。因此,它警告您在使用它之前对其进行初始化,例如int x = 0; // or any value for that matter
  • @akuzminykh 没有导致x 未初始化的代码路径,但编译器没有感知能力,并且使用外部boolean 保护变量进行流控制检测对它来说有点太多了分析(可怜的东西)。
  • 对。知道 'x' 被有效设置需要跟踪,如果 x 未在循环中设置,则 found 仍然为 false,因此 x 将在循环后设置。这要求有点多。

标签: java initialization


【解决方案1】:

编译器无法轻易检测到导致 x 被初始化的所有分支,但您可以通过将 -1 分配给 x 来轻松修复该问题(和代码)。类似的东西

public static void main(String[] args) {
    int x = -1;
    for (int i = 0; i < 10; i++) {
        if (Math.random() < 0.5) {
            x = 10;
            break;
        }
    }
    System.out.println(x);
}

现在你不需要found(所以我也删除了它)。

【讨论】:

  • 所以在实践中,x 总是会被初始化(在我的代码中)?
  • 这段代码更好,不仅因为它避免了编译器警告,而且(在我看来更重要的是)它不需要额外的标志和对该标志的测试,使代码更简单,因此更明显是正确的。一般技术可能被描述为“初始化为默认值”。
  • @lineage - “在实践中......” - 是的,在你的情况下这是一个错误的警告,但这是我们为让编译器尝试为我们找到未初始化变量错误所付出的代价。有时你只需要提供一个你不需要的初始化。但是,在这种情况下,由于其他原因,进行了重构。
  • @iggy 解决了它。由于其他原因,我需要外部标志,因此重构实际上并没有帮助我。我提出了这个问题,因为我一直认为编译器会通过所有可以在不运行代码的情况下得出的关于代码的逻辑结论......似乎并非如此
  • @iggy 用 ElliotFrisch 的话来说,可怜的东西
【解决方案2】:

(将其写为单独的答案,因为我认为从 cmets 中取出它会受益)。

这是语言律师的回答。

变量使用前的语言规范requires initialization

规则包括:

  1. 必须在代码的所有可能路径上为变量赋值。规范将此称为“明确分配”。

  2. 编译器在此分析中不考虑表达式的值。参见示例 16.2。

第二条规则解释了为什么即使在对我们来说“显而易见”的情况下,编译器也不能使用这些知识。即使编译器编写者愿意进行更深入的分析,遵守 Java 规范也不允许这样做。

如果下一个问题是“但是为什么?”那么我不得不猜测,但标准的重点是获得一致的行为。您不希望一个编译器接受另一个编译器拒绝的合法 Java。

【讨论】:

    猜你喜欢
    • 2015-02-20
    • 2012-07-28
    • 1970-01-01
    • 2022-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多