【问题标题】:Why isn't a qualified static final variable allowed in a static initialization block?为什么静态初始化块中不允许有限定的静态最终变量?
【发布时间】:2012-11-26 13:12:03
【问题描述】:

案例一

class Program {
    static final int var;

    static {
        Program.var = 8;  // Compilation error
    }

    public static void main(String[] args) {
        int i;
        i = Program.var;
        System.out.println(Program.var);
    }
}

案例 2

class Program {
    static final int var;

    static {
        var = 8;  //OK
    }

    public static void main(String[] args) {
        System.out.println(Program.var);
    }
}

为什么案例1会导致编译错误?

【问题讨论】:

  • 案例 2 有效,为什么要关心?
  • 它绝对看起来像访问外国决赛(这是非法的)。
  • 非常棘手的好问题,现在给大脑工作.. :)
  • @RohitJain 老实说,我不会称这是一个很好的面试问题:您无法根据程序员是否知道该问题的答案得出任何关于程序员水平的结论!
  • 这是我听过的最无用的问题之一 :) 我不明白为什么 ppl 试图让编译器脱离程序员。我最好问一下平衡搜索树和布隆过滤器:)

标签: java static final static-initialization qualified-name


【解决方案1】:

JLS 拥有答案(注意粗体声明):

同样,每个空白的 final 变量最多只能赋值一次;当对它进行分配时,它必须绝对未分配当且仅当变量的简单名称(或者,对于字段,其简单名称由此限定)出现在赋值运算符的左侧时,才会定义这样的赋值。 [§16]

这意味着在分配静态最终变量时必须使用“简单名称” - 即没有任何限定符的 var 名称。

【讨论】:

  • @djechlin - 理由是 1) 这简化了规范 2) ThisClass.someFinal = value;someFinal = value; 的一个晦涩且不必要的替代方案;即没有需要来支持它。
  • @coders 在所有初始化完成之前,该类按名称不可用,因此在静态初始化程序完成之前,程序(按该名称)不存在。
  • @xagyg.. 恐怕这不是原因。类总是首先加载,然后进行初始化过程。静态变量只附加到一个类,所以我们不能使用class name 是没有意义的。您可以自己在实践中看到它。只需从变量中删除final 关键字,第一个代码就可以正常编译。
  • @StephenC:它实际上使规范复杂化,因为它是静态变量的一般访问规则的一个例外。如果没有这个特殊规则,粗体字可以省略。我认为这样做的理由可能是它只是在早期的编译器中以这种方式实现,然后将其纳入规范。在语言规范的后续版本中还有一些其他规则,其中编译器的现有稍微模糊的行为被明确定义为正确的。
  • 很抱歉参加聚会迟到了,但我认为代码完全没有违反 JLS 的这一点。 JLS 引用只是说Program.var = 8 不是“这样的分配”,即,它需要首先明确取消分配的那种分配。
【解决方案2】:

显然,这是一种廉价的句法技巧,可以限制类本身的明确(非)赋值分析。

如果该字段在语法上用类名限定,则代码通常在另一个类中,分析无法到达。

这个技巧在你的例子中失败了。其他奇怪的例子:

static class A
{
    static final int a;
    static
    {
        // System.out.println(a); // illegal
        System.out.println(A.a);  // compiles!
        a = 1;
    }
}

如果他们有更多的资源,他们可能会制定更好的规则。但我们现在不能更改规格。

【讨论】:

  • 好吧,我认为规范可以更改而不会引起问题,因为允许Program.var 不会使任何当前有效的程序代码无效,即它是兼容更改,或者我错了吗?
  • :-D 没问题,只是觉得我遗漏了什么……谢谢你的回答!
  • @siegi:扩展编译器接受的程序类将增加字段代码的数量,这些代码在旧版编译器安装中不可用。如果字段代码由于使用旧编译器不理解的有价值的新语言功能而无法在旧编译器上工作,那么这是一个可以接受的权衡,这是一个的权衡。允许没有主要优势的新句法形式会给旧设备的所有者带来成本,而给他们的回报却很少。
  • @supercat:你当然是对的,改变语言没有太多好处是不好的。另一方面:如果语言更改与新的主要版本(例如 Java 6 到 Java 7)同时发生,那么无论如何您都需要新的编译器和运行时环境,至少如果您想使用任何新功能或编译的任何库新版本……
  • 这里的可怕之处在于,如果你在上面的a = 1; 之后添加System.out.println(A.a);,并输入一个main,它实际上会访问并打印出0 和1:ideone.com/Is80fA Youch .在最终值之前观察最终值。
猜你喜欢
  • 2013-09-30
  • 2017-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-13
  • 2015-09-06
相关资源
最近更新 更多