When declaring a local inner class inside a method, why is it legal to include final static Strings or ints but not legal to include other objects?
For instance:
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();
}
}
When I compile this, I get the following:
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();
^
Why the distinction? Is it because String is immutable? If so, wouldn't Integer.valueOf() also be valid?
It's because the first two static members are assigned to compile-time constants of primitive type or type String.
From the Java Language Specification, section 8.1.3:
8.1.3. Inner Classes and Enclosing Instances
Inner classes may not declare static members, unless they are constant variables (§4.12.4), or a compile-time error occurs.
And from 4.12.4:
A variable of primitive type or type String, that is final and initialized with a compile-time constant expression (§15.28), is called a constant variable.
EDIT:
I found this surprising at first. Thinking about it more, one advantage to this limitation is that there is no need to worry about when static members of inner classes are initialized. You can move an inner class around in its containing class, without concern that the values of its static members will be changed.
More on the previous answer. The value assigned must be provable by the compiler that it is a constant. The Java compiler knows the semantics of the base types (int, float, etc.) and the java.lang.String class, but not other classes. This is can understand the constantness of the first two examples.
The compiler does not understand that Integer.valueOf(3) is also (effectively) a constant (actually not constant, but always the same) value even though a human, that knows how the Integer class works, does know that. The compiler treats this as if it was Integer.valueOf(x) which can change. It would be nice if Java offered an annotation, such as @interface Consistent, that declared the method behavior as stable for any give parameters, such as:
In Integer class:
@Consistent public Integer valueOf(int x) {...}
final static Integer intThree = Integer.valueOf(3); // now compiles!
That indicates the method returns either the same or an equal object on each invocation given the same argument values. Since the argument is a constant expression, the compiler can deduce that the result will be the same/equal in all usage and can thus be treated as a constant. In this case Integer returns the same object, but it can return a different (but equal) object for much lager input values (ie. it caches values near 0).
Note that "new" always returns a different object. For new Object() it is always an object not equal to any other object.
Consider the definition of a compile-time constant expression from 15.28:
A compile-time constant expression is an expression denoting a value of primitive type
or a String that does not complete abruptly and is composed using only the following:
- Literals of primitive type and literals of type
String
(§3.10.1, §3.10.2, §3.10.3,
§3.10.4, §3.10.5)
- Casts to primitive types and casts to type
String
(§15.16)
- The unary operators
+
, -
, ~
, and !
(but not ++
or --
) (§15.15.3, §15.15.4,
§15.15.5, §15.15.6)
- The multiplicative operators
*
, /
, and %
(§15.17)
- The additive operators
+
and -
(§15.18)
- The shift operators
<<
, >>
, and >>>
(§15.19)
- The relational operators
<
, <=
, >
, and >=
(but not instanceof
) (§15.20)
- The equality operators
==
and !=
(§15.21)
- The bitwise and logical operators
&
, ^
, and |
(§15.22)
- The conditional-and operator
&&
and the conditional-or operator ||
(§15.23, §15.24)
- The ternary conditional operator
? :
(§15.25)
- Parenthesized expressions (§15.8.5) whose contained expression is a constant expression.
- Simple names (§6.5.6.1) that refer to constant variables (§4.12.4).
- Qualified names (§6.5.6.2) of the form TypeName . Identifier that refer to constant
variables (§4.12.4).
Following from the definition of a compile-time constant expression, we have 4.12.4:
A variable of primitive type or type String
, that is final
and initialized with a compile-time constant expression (§15.28), is called a constant variable.
Finally, from 8.1.3:
Inner classes may not declare static members, unless they
are constant variables (§4.12.4), or a compile-time error occurs.