54

Can someone explain to me why the first of the following two samples compiles, while the second doesn't? Notice the only difference is that the first one explicitly qualifies the reference to x with '.this', while the second doesn't. In both cases, the final field x is clearly attempted to be used before initialized.

I would have thought both samples would be treated completely equally, resulting in a compilation error for both.

1)

public class Foo {
    private final int x;
    private Foo() {
        int y = 2 * this.x;
        x = 5;
    }
}

2)

public class Foo {
    private final int x;
    private Foo() {
        int y = 2 * x;
        x = 5;
    }
}
weiresr
  • 610
  • 7
  • 12
  • `NetBeans` gives me an error in both cases. Are you sure? – Petar Minchev Dec 13 '12 at 16:48
  • Eclipse 4.2 (JDK7u9) does exactly what OP describes. – Petr Janeček Dec 13 '12 at 16:48
  • 3
    @PetarMinchev It compiles for me from the command line with the Java 1.7.0 compiler – Hunter McMillen Dec 13 '12 at 16:49
  • OK, maybe NetBeans is smart:) – Petar Minchev Dec 13 '12 at 16:49
  • Eclipse gives me the error with java 1.7. Eclipse issue anyone? – LPD Dec 13 '12 at 16:53
  • 8
    This isn't a complete answer, so, posting as a comment: It makes sense that the first one would compile, because [§17.5.2 "Reading `final` Fields During Construction" of the *Java Language Specification*, Java SE 7 Edition](http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.2) does specify what happens if you read a `final` field before it's initialized (specifically: you see the default value, in this case 0). But I have no idea why the second one *doesn't* compile. :-P – ruakh Dec 13 '12 at 16:53
  • 1
    Remember, Eclipse uses it's own proprietary compiler... – durron597 Dec 13 '12 at 16:55
  • `javac 1.7.0_09` also does exactly what OP describes. – dogbane Dec 13 '12 at 16:56
  • 4
    Jon Skeet is laughing at us now. He will come soon like a boss:P – Petar Minchev Dec 13 '12 at 16:59
  • 5
    This *also* isn't a complete answer, so, posting as another comment: It also makes sense that the second one doesn't compile, because [Chapter 16 "Definite Assignment" of the *Java Language Specification*, Java SE 7 Edition](http://docs.oracle.com/javase/specs/jls/se7/html/jls-16.html) says that a blank `final` field ("blank" meaning, its declaration didn't have an initializer) must be definitely assigned before every access, just as with local variables. (Yes, this contradicts my previous comment; but I blame the JLS for being confusing!) – ruakh Dec 13 '12 at 17:01
  • we already know that eclipse compiler treats it differently than oracle jdk. anybody can test in on ibm's jdk? It probably is more a compiler quirk than something that follows from language specification. – soulcheck Dec 13 '12 at 17:08
  • (See also having to unqualified field names when assigning `static final`s in a static initialiser. Also consider `Foo() { Foo me = this; x = me.x; }`.) – Tom Hawtin - tackline Dec 13 '12 at 17:27
  • 2
    possible duplicate - http://stackoverflow.com/questions/13778650/why-isnt-a-qualified-static-final-variable-is-allowed-in-a-static-initializatio – Ravi Dec 14 '12 at 02:55

4 Answers4

39

After a bunch of spec-reading and thought, I've concluded that:

In a Java 5 or Java 6 compiler, this is correct behavior. Chapter 16 "Definite Assignment of The Java Language Specification, Third Edition says:

Each local variable (§14.4) and every blank final (§4.12.4) field (§8.3.1.2) must have a definitely assigned value when any access of its value occurs. An access to its value consists of the simple name of the variable occurring anywhere in an expression except as the left-hand operand of the simple assignment operator =.

(emphasis mine). So in the expression 2 * this.x, the this.x part is not considered an "access of [x's] value" (and therefore is not subject to the rules of definite assignment), because this.x is not the simple name of the instance variable x. (N.B. the rule for when definite assignment occurs, in the paragraph after the above-quoted text, does allow something like this.x = 3, and considers x to be definitely assigned thereafter; it's only the rule for accesses that doesn't count this.x.) Note that the value of this.x in this case will be zero, per §17.5.2.

In a Java 7 compiler, this is a compiler bug, but an understandable one. Chapter 16 "Definite Assignment" of the Java Language Specification, Java 7 SE Edition says:

Each local variable (§14.4) and every blank final field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs.

An access to its value consists of the simple name of the variable (or, for a field, the simple name of the field qualified by this) occurring anywhere in an expression except as the left-hand operand of the simple assignment operator = (§15.26.1).

(emphasis mine). So in the expression 2 * this.x, the this.x part should be considered an "access to [x's] value", and should give a compile error.

But you didn't ask whether the first one should compile, you asked why it does compile (in some compilers). This is necessarily speculative, but I'll make two guesses:

  1. Most Java 7 compilers were written by modifying Java 6 compilers. Some compiler-writers may not have noticed this change. Furthermore, many Java-7 compilers and IDEs still support Java 6, and some compiler-writers may not have felt motivated to specifically reject something in Java-7 mode that they accept in Java-6 mode.
  2. The new Java 7 behavior is strangely inconsistent. Something like (false ? null : this).x is still allowed, and for that matter, even (this).x is still allowed; it's only the specific token-sequence this plus . plus the field-name that's affected by this change. Granted, such an inconsistency already existed on the left-hand side of an assignment statement (we can write this.x = 3, but not (this).x = 3), but that's more readily understandable: it's accepting this.x = 3 as a special permitted case of the otherwise forbidden construction obj.x = 3. It makes sense to allow that. But I don't think it makes sense to reject 2 * this.x as a special forbidden case of the otherwise permitted construction 2 * obj.x, given that (1) this special forbidden case is easily worked around by adding parentheses, that (2) this special forbidden case was allowed in previous versions of the language, and that (3) we still need the special rule whereby final fields have their default values (e.g. 0 for an int) until they're initialized, both because of cases like (this).x, and because of cases like this.foo() where foo() is a method that accesses x. So some compiler-writers may not have felt motivated to make this inconsistent change.

Either of these would be surprising — I assume that compiler-writers had detailed information about every single change to the spec, and in my experience Java compilers are usually pretty good about sticking to the spec exactly (unlike some languages, where every compiler has its own dialect) — but, well, something happened, and the above are my only two guesses.

Community
  • 1
  • 1
ruakh
  • 156,364
  • 23
  • 244
  • 282
  • @GanGnaMStYleOverFlowErroR you are missing that ruakh says that it shouldn't compile (due to spec change for Java 7), but do compiles because of a bug in Java 7 compiler – Andrii Polunin Dec 13 '12 at 19:40
  • @irreputable: Yup, looks like another consequence of the same bit of language legalese. Thanks for the link! – ruakh Dec 13 '12 at 19:45
  • when a programmer accesses `this.x`, usually he thinks it has been assigned; therefore if it's not actually the case, a compiler warning/error would be helpful. – irreputable Dec 13 '12 at 19:58
  • 1
    thanks for the extensive explanation! I haven't been looking a lot into the depths of the JLS specs myself, I have to say. Experienced it on Java 6 compiler, btw – weiresr Dec 13 '12 at 20:03
  • Coming here off of [this](https://stackoverflow.com/questions/24240215/java-compiler-allows-accessing-uninitialized-blank-final-field-using-this-keyw) question. This behavior still occurs in Java 1.8.0_05 for both `javac` and Eclipse's compiler. Seems to be a strangely persistent bug... – awksp Jun 16 '14 at 09:40
  • [Eclipse bug 480989](https://bugs.eclipse.org/bugs/show_bug.cgi?id=480989) filed from this, thanks! – Tomas Oct 29 '15 at 13:11
2

When you use this in the constructor, compiler is seeing x as a member attribute of this object (default initialized). Since x is int, it's default initialized with 0. This makes compiler happy and its working fine at run time too.

When you don't use this, then compiler is using x declaration directly in the lexical analysis and hence it complains about it's initialization (compile time phenomenon).

So It's definition of this, which makes compiler to analyze x as a member variable of an object versus direct attribute during the lexical analysis in the compilation and resulting into different compilation behavior.

When used as a primary expression, the keyword this denotes a value that is a reference to the object for which the instance method was invoked (§15.12), or to the object being constructed.

Yogendra Singh
  • 32,067
  • 6
  • 59
  • 71
  • 4
    Perhaps I'm missing something, but I don't see how your quote explains this. – Pablo Dec 13 '12 at 16:58
  • 1
    @Pablo: What I am trying to say is: `this` technical refers the object instance. Then when you access `x` through `this`, it uses the default initialization of `x`. – Yogendra Singh Dec 13 '12 at 17:00
  • I get this _"`this` technical refers the object instance"_. But I don't see how the second part _"Then when you access x through `this`, it uses the default initialization of x"_ follows from your quote. – Pablo Dec 13 '12 at 17:03
  • I have always thought (as I learnt in some SCJP book) that `this` is implicit when not written =/. – Petr Janeček Dec 13 '12 at 17:05
  • 5
    `x` being an instance variable, accessing it with or without `this` should not yield different behaviors, no?. – Laf Dec 13 '12 at 17:08
  • 2
    @YogendraSingh and if instead we use `int y = 2 * 0;` and inspect x, it also has 0, even if we are not using `this` anywhere in the code. The debugger doesn't prove that your explanation is correct. X has to be initialized to 0 because is a primitive int value, those are always initialized to 0. – Pablo Dec 13 '12 at 17:16
  • 1
    @YogendraSingh my second question is just that _"Then when you access x through `this`, it uses the default initialization of x"_ doesn't follow from _"When used as a primary expression, the keyword this denotes a value that is a reference to the object for which the instance method was invoked (§15.12), or to the object being constructed"_. – Pablo Dec 13 '12 at 17:19
-1

I think the compiler estimates that writing this.x implies 'this' exists, so a Constructor has been called (and final variable has been initialized). But you should get a RuntimeException when trying to run it

Slauster
  • 99
  • 1
-1

I assume you refer to the behaviour in Eclipse. (As stated as comment a compile with javac works).

I think this is an Eclipse problem. It has its own compiler, and own set of rules. One of them is that you may not access a field which is not initialized, although the Java-commpiler would initialize variables for you.

Andy
  • 1,884
  • 1
  • 14
  • 27