【问题标题】:Java final static declarations in method local classes方法本地类中的 Java 最终静态声明
【发布时间】:2013-06-19 16:35:01
【问题描述】:

在方法内部声明局部内部类时,为什么包含 final static Strings 或 int 是合法的,但包含其他对象是不合法的?

例如:

class Outer {
void aMethod() {
    class Inner {
        final static String name = "compiles";
        final static int ctr = 10; // compiles
        final static Integer intThree = Integer.valueOf(3); // does not compile!
        final static obj objConst = new Object(); // does not compile!
    }

    Inner inner = new Inner();
}
}

当我编译这个时,我得到以下信息:

InnerExample.java:6: inner classes cannot have static declarations
        final static Integer outer = Integer.valueOf(3);
                             ^
InnerExample.java:7: inner classes cannot have static declarations
        final static Object objConst = new Object();
                            ^

为什么要区分?是因为 String 是不可变的吗?如果是这样,Integer.valueOf() 不是也有效吗?

【问题讨论】:

  • 我很确定这是因为“compiles”和 10 是编译时常量表达式,但我还没有找到 JLS 规则。

标签: java inner-classes final static-members


【解决方案1】:

这是因为前两个静态成员被分配给原始类型或字符串类型的编译时常量。

来自Java Language Specification, section 8.1.3

8.1.3。内部类和封闭实例

内部类不能声明静态成员,除非它们是常量变量(第 4.12.4 节),或者发生编译时错误。

来自4.12.4

原始类型或 String 类型的变量,它是 final 并使用编译时常量表达式(第 15.28 节)初始化,称为常量变量。

编辑:

起初我觉得这很令人惊讶。再想一想,这个限制的一个好处是不用担心什么时候初始化内部类的静态成员。您可以在其包含类中移动内部类,而不必担心其静态成员的值会被更改。

【讨论】:

    【解决方案2】:

    更多关于上一个答案。分配的值必须可由编译器证明它是一个常量。 Java 编译器知道基本类型(int、float 等)和 java.lang.String 类的语义,但不知道其他类。这样就可以理解前两个例子的不变性了。

    编译器不理解 Integer.valueOf(3) 也是(实际上)一个常量(实际上不是常量,但始终相同)值,即使知道 Integer 类如何工作的人也知道这一点。编译器将其视为可以更改的 Integer.valueOf(x) 。如果 Java 提供一个注解,例如 @interface Consistent,声明方法行为对于任何给定参数都是稳定的,那就太好了,例如:

    在整数类中: @Consistent public Integer valueOf(int x) {...}

    final static Integer intThree = Integer.valueOf(3); // 现在编译!

    这表明在给定相同参数值的情况下,该方法在每次调用时返回相同或相等的对象。由于参数是一个常量表达式,编译器可以推断出结果在所有用法中都是相同/相等的,因此可以被视为一个常量。在这种情况下,Integer 返回相同的对象,但它可以为更大的输入值返回不同(但相等)的对象(即,它缓存接近 0 的值)。

    请注意,“new”总是返回不同的对象。对于 new Object(),它始终是一个不等于任何其他对象的对象。

    【讨论】:

    • 这是一个很棒的解释,有助于回答第二部分,即如何理解 Integer.valueOf() 与编译时间常数相关的操作。
    【解决方案3】:

    考虑来自15.28编译时常量表达式的定义:

    编译时常量表达式是表示原始类型值的表达式 或一个不会突然完成且仅使用以下内容组成的字符串:

    • 原始类型的文字和String 类型的文字(§3.10.1、§3.10.2、§3.10.3、 §3.10.4, §3.10.5)
    • 转换为基本类型并转换为 String 类型(第 15.16 节)
    • 一元运算符+-~!(但不是++--)(§15.15.3、§15.15.4、 §15.15.5、§15.15.6)
    • 乘法运算符 */%(第 15.17 节)
    • 加法运算符 +- (§15.18)
    • 移位运算符 <<>>>>>(第 15.19 节)
    • 关系运算符<<=>>=(但不是instanceof)(§15.20)
    • 相等运算符 ==!= (§15.21)
    • 按位和逻辑运算符 &^|(第 15.22 节)
    • 条件与运算符&& 和条件或运算符||(§15.23、§15.24)
    • 三元条件运算符? : (§15.25)
    • 带括号的表达式(第 15.8.5 节),其包含的表达式是常量表达式。
    • 引用常量变量 (§4.12.4) 的简单名称 (§6.5.6.1)。
    • TypeName 形式的限定名称(§6.5.6.2)。引用常量的标识符 变量(第 4.12.4 节)。

    根据编译时常量表达式的定义,我们有4.12.4

    原始类型或 String 类型的变量,即 final 并使用编译时常量表达式(第 15.28 节)初始化,称为常量变量。

    最后来自8.1.3

    内部类不能声明静态成员,除非它们 是常量变量(第 4.12.4 节),否则会发生编译时错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-11-07
      • 1970-01-01
      • 2011-06-02
      • 2019-04-14
      • 2014-11-09
      • 2010-12-28
      • 2014-05-12
      相关资源
      最近更新 更多