Java: Why no warning when referencing a field befo

2019-04-20 18:40发布

A static field cannot be referenced before it is defined or initialized:

static Integer j = i; /* compile error */
static final Integer i = 5;

However, when it is referenced from an instance initialization block (in an anonymous inner class), not even a warning is generated.

See example:

class StaticInitialization {

    static final Object o = new Object() {{
        j = i;
    }};

    static Integer j, k;
    static final Integer i = 5;

    static final Object o2 = new Object() {{
        k = i;
    }};
}

The result is: j == null, k == 5, so clearly we've made a reference, order matters, and no warning or compilation error.

Is this code legal?

6条回答
别忘想泡老子
2楼-- · 2019-04-20 18:54

Yes, that happens because first the space for all variables is allocated (and initialized with #0) and after that all the initialization is done in the order of your code.

Means, if you change the code like this:

    class StaticInitialization {

    static Integer j, k;
    static final Integer i = 5;

    static final Object o = new Object() {{
        j = i;
    }};

    static final Object o2 = new Object() {{
        k = i;
    }};
    }

The result is for all variables == 5.

查看更多
贼婆χ
3楼-- · 2019-04-20 19:11

Is this code legal? Probably. I don't think it's the compiler's job to analyze your deliberate side-effects of object instantiation in buggering up static variables.

The limited analysis of 'declare before referencing' from other statics in the same class is really just a helper against the most common boo-boos, not a ironclad guarantee against indirect errors.

I'm really not at all surprised that the the "declare before referencing" analysis is limited in scope to direct access of static variables in other static declarations. This is a simple & compact analysis with minimal complexity & very fast.

Extending it to consider side-effects of object instantiation & method calls, OTOH, would require the 20-1000x greater weight & scope of static program analysis. Static analysis requires access to potentially the entire compiled program code, with constraint-based computation to determine what may possibly happen, and run-times potentially in the minutes.

Given these choices it is fairly easy, in the Java language designer's shoes, to choose simple analysis covering only direct accesses from fields within the same class.

查看更多
走好不送
4楼-- · 2019-04-20 19:12

Your code is similar to this one:

class StaticInitialization
{
    static final Foo1 o = new Foo1();
    static Integer j, k;
    static final Integer i = 5;
    static final Foo2 o = new Foo2();

    class Foo1
    {
        public Foo1()
        {
            j = i;
        }
    }

    class Foo2
    {
        public Foo2()
        {
            k = i;
        }
    }
}

When you refer to i inside Foo1, i was null. However, i becomes 5 when you refer to it inside Foo2. Note that if i was a compile-constant (i is int not Integer), then j would be 5.

See this related question: Creating an object in a static way

查看更多
啃猪蹄的小仙女
5楼-- · 2019-04-20 19:15

The rules for final variables are quite different from all others. For example, for final variables the compiler must check if the variable has definitely been assigned once and not been assigned afterwards.

This is possible, because there are quite a few restrictions on final variables (which is pretty much the point of final).

Therefore, the in-depth analysis of "is this assigned before use" only really works within the limited world of final variables.

查看更多
疯言疯语
6楼-- · 2019-04-20 19:17

In the first block, you're not referencing a static field before it is initialized, but referencing before it is defined (as the compiler will tell you). This will work, for example:

static Integer j;
static final Integer i = j;

All fields have default values when not set explicitly (for objects, that's the null reference, for primitives, that's the corresponding sensible default, 0, false, etc.). Therefore, the fields are always initialized so the compiler doesn't need to check for that.

Regarding final: it's really a modifier used for the benefit of the developer(s). As the JSL states, final fields must be "definitely assigned" before access. That does not mean they would have no value otherwise (if it were not final), it just means that the compiler protects you against not assigning it explicitly, if it cannot find that assignment.

So, for non-final fields, you most definitely can reference them before an explicit assignment. Just paste the code snippet above to verify.

查看更多
我欲成王,谁敢阻挡
7楼-- · 2019-04-20 19:17

This is defined in JLS 8.3.2.3.

The declaration of a member needs to appear textually before it is used only if the member is an instance.

That is why you are getting error when you do this..

static Integer j = i; /* compile error */
static final Integer i = 5;

But accesses by methods are not checked in the same way.

查看更多
登录 后发表回答