16

在方法中声明本地内部类时,为什么包含 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() 不是也有效吗?

4

3 回答 3

17

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

来自Java 语言规范,第 8.1.3 节

8.1.3. 内部类和封闭实例

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

4.12.4开始:

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

编辑:

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

于 2013-06-22T18:34:30.497 回答
5

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

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

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

最终静态整数 intThree = Integer.valueOf(3); // 现在编译!

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

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

于 2013-06-22T19:03:24.233 回答
3

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

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

  • 原始类型的文字和类型的文字String(§3.10.1、§3.10.2、§3.10.3、§3.10.4、§3.10.5)
  • 转换为原始类型和转换为类型String(第 15.16 节)
  • 一元运算符+, -, ~, and !(但不是++or --) (§15.15.3, §15.15.4, §15.15.5, §15.15.6)
  • 乘法运算符*, /, 和%(§15.17)
  • 加法运算符+-(§15.18)
  • 移位运算符<<>>>>>(§15.19)
  • 关系运算符<, <=, >, and >=(but not 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 节),或者发生编译时错误。

于 2013-06-22T18:45:35.287 回答