方法本地类中的Java final静态声明

在方法中声明本地内部类时,为什么包含最终的静态字符串或整数是合法的,但包含其他对象是不合法的?

例如:

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()也不会有效吗?

这是因为前两个静态成员被分配给基本类型或类型String的编译时常量。

从Java语言规范,第8.1.3节 :

8.1.3。 内部类和封闭实例

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

从4.12.4开始 :

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

编辑:

我一开始觉得这很令人惊讶。 更多地考虑它,这个限制的一个优点是没有必要担心内部类的静态成员何时被初始化。 您可以在其包含的类中移动内部类,而不必担心其静态成员的值将被更改。

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

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

在Integer类中:@Consistent public Integer valueOf(int x){…}

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

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

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

考虑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节),其包含的表达式是常量表达式。
  • 引用常量变量的简单名称(第6.5.6.1节)(§4.12.4)。
  • TypeNameforms的限定名称(第6.5.6.2节) 引用常量变量的标识符 (§4.12.4)。

编译时常量表达式的定义开始,我们有4.12.4 :

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

最后,从8.1.3开始 :

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