在方法中声明一个本地内部类时,为什么包含最终的静态字符串或整数是合法的,但包含其他对象是不合法的呢?
例如:
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()也不会有效吗?
答案 0 :(得分:17)
这是因为前两个静态成员被分配给基本类型或类型String的编译时常量。
来自Java Language Specification, section 8.1.3:
8.1.3。内部类和封闭实例
内部类可能不会声明静态成员,除非它们是常量变量(第4.12.4节),否则会发生编译时错误。
来自4.12.4:
基本类型或类型String的变量,是最终的并使用编译时常量表达式(第15.28节)初始化,称为常量变量。
修改强>
我起初发现这令人惊讶。考虑到这一点,这个限制的一个优点是无需担心内部类的静态成员何时被初始化。您可以在其包含的类中移动内部类,而不必担心其静态成员的值将被更改。
答案 1 :(得分:5)
更多关于上一个答案。分配的值必须由编译器证明它是常量。 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(),它总是一个不等于任何其他对象的对象。
答案 2 :(得分: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节)。
- 引用常量变量的简单名称(第6.5.6.1节)(§4.12.4)。
- TypeName格式的合格名称(第6.5.6.2节)。引用常量的标识符 变量(§4.12.4)。
根据编译时常量表达式的定义,我们有4.12.4:
基本类型或类型
String
的变量,即final
,并使用编译时常量表达式(第15.28节)初始化,称为常量变量。
最后,来自8.1.3:
内部类可能不会声明静态成员,除非它们 是常量变量(§4.12.4),或发生编译时错误。