constant variable that's not static

33 views
Skip to first unread message

zhong...@gmail.com

unread,
Sep 8, 2015, 4:42:20 AM9/8/15
to java.lang.fans
Usually, constant variables are static, e.g.

    static final int x = 1;

JLS guarantees[1] that nobody can observe its default value `0`. Practically speaking, static constant variables don't really have default values; JVM implementations do not need to zero these fields when the class is initialized.

--

Now, the funny thing is, an instance variable can also be a constant variable [2].

    final int x = 1;

Obviously, it doesn't make sense to include a constant field per instance; the field ought to be static. But there might be a reason why the programmer chooses to make it an instance field at this point of time.

Anyways, the field is initialized in textual order; the default value `0` can be observed, for example, by reflections.

However, I don't see any non-reflection way of observing the default value `0`. The following program will print `1`

    class Test
    {
        {
            System.out.println(this.x);  // x==0 here, if read by reflection
        }

        final int x = 1;
    }

In fact, any `expr.x` will evaluate to `1`, as long as the type of `expr` is `Test`. (Though, if `expr` is null, javac diligently triggers a NullPointerException.)

This makes perfect sense. However, JLS only considers the simple name `x` as a constant expression [3]; `this.x` is not a constant expression, and it should not be inlined as `1`. So it's a javac bug. But we could also blame JLS here for being incomplete.


[1] http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.3.2
[2] http://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.12.4
[3] http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.28

--
Zhong Yu


y s

unread,
Sep 8, 2015, 10:55:59 PM9/8/15
to zhong...@gmail.com, java.lang.fans
I think javap is only in error for the initializer block. In any other context, inlining the value is fine.

javap's job is to generate code that will result in what the JLS defines as correct execution. It doesn't have to account for reflection, since JLS 1.4 explicitly says that the JLS "does not describe reflection in any detail" -- so all bets are off! (To my knowledge, the JLS doesn't specify behavior in the case of recompiling a dependency; in fact, it doesn't even specify that you have to compile to .class files!)

What the JLS does specify is that the write of "x = 1" is visible to all threads, since "x" is final. It also specifies that there can be no further writes to "x" except by reflection, which is unspecified and thus not a concern of javap's. Putting those together, it's fine to inline the value of 1, since it results in an expression evaluation that's identical to actually reading the field -- unless that read happens in an initializer prior to the write, which is indeed a bug!

Incidentally, it's funny that javap handles that inlining by loading the instance and invoking getClass() on it -- presumably as a quick way of triggering an NPE if it's null. Makes for some odd bytecode. :)

--
You received this message because you are subscribed to the Google Groups "java.lang.fans" group.
To unsubscribe from this group and stop receiving emails from it, send an email to java-lang-fan...@googlegroups.com.
To post to this group, send email to java-la...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/java-lang-fans/65cb7d77-7b0e-4b44-a566-efae2d5eb4f5%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Zhong Yu

unread,
Sep 9, 2015, 11:55:17 AM9/9/15
to y s, java.lang.fans

On Tue, Sep 8, 2015 at 9:55 PM, y s <eleus...@gmail.com> wrote:
I think javap is only in error for the initializer block. In any other context, inlining the value is fine.



If `this` is leaked during initialization to another code, which then accesses `that.x`, what should it read? I think JLS says it should read `0`, if the read is on the same thread, before the write.

But if the access is done by `that.getX()`, and `getX()` is

    public int getX(){ return x; }

it should return `1`, because the simple name `x` is a constant expression here :)

This part of the spec is certainly wobbly. But it doesn't matter since nobody should have non-static constant variables anyway.

Zhong Yu

y s

unread,
Sep 9, 2015, 3:14:29 PM9/9/15
to Zhong Yu, java.lang.fans
Actually, re-reading the spec, I'm not sure javac is in error here. Section 15.28 says that a constant expression includes names that refer to constant variables, and section 4.12.4 says that any final variable can a constant variable -- no requirement for static final (it just needs to be assigned the value of a constant expression).

So, it looks like test.x really is a constant expression, and inlining it is fine.

Except that...

The spec says that constant expressions include simple names (without qualifications on static-ness) and "Qualified names of the form TypeName . Identifier." A strict reading of that would suggest that "Test.x" is a constant expression if Test is a TypeName (thus implying that x is static), and that "x" is a constant expression, but that "instance.x" is not (since it's a qualified name, but not of TypeName . Identifier).

    final int x = 123; // an instance field
    ...
    int var1 = x; // constant expression
    int var2 = this.x // not a constant expression?!

I can't believe this is what they intended, so what they did intend is anyone's guess. :)

Zhong Yu

unread,
Sep 9, 2015, 4:08:02 PM9/9/15
to y s, java.lang.fans
On Wed, Sep 9, 2015 at 2:14 PM, y s <eleus...@gmail.com> wrote:
The spec says that constant expressions include simple names (without qualifications on static-ness) and "Qualified names of the form TypeName . Identifier." A strict reading of that would suggest that "Test.x" is a constant expression if Test is a TypeName (thus implying that x is static), and that "x" is a constant expression, but that "instance.x" is not (since it's a qualified name, but not of TypeName . Identifier).

    final int x = 123; // an instance field
    ...
    int var1 = x; // constant expression
    int var2 = this.x // not a constant expression?!


Right, if we use `this.x` where a constant expression is expected, for example, `case this.x`, javac will complain that it's not a constant expression.

Nevertheless, where `this.x` is legal, javac replaces it with constant `1`. This is usually correct, except before `x` is assigned...

Zhong Yu
 

Zhong Yu

unread,
Sep 9, 2015, 4:19:19 PM9/9/15
to y s, java.lang.fans
Also, a local variable can also be a constant, which is of course very useful

    void foo()
    {
        final int BAR = 1;
      ...

It can only be accessed by simple name, so no problem there.

There are 8 kinds of variables (jls#4.12.3); only class variable, instance variable, local variable could be constant variables.


Zhong Yu
bayou.io

y s

unread,
Sep 9, 2015, 4:52:14 PM9/9/15
to Zhong Yu, java.lang.fans
There are 10 kinds of variables: those that understand base 7, and those that don't... or something? ;)
Reply all
Reply to author
Forward
0 new messages