问题
I found happening with the following code made my jaw drop:
public class MCVE {
{ // instance initializer
System.out.println(test); // cannot reference a field before it is defined
System.out.println(this.test);
}
private final String test = "wat";
}
The line System.out.println(test);
is giving the error
Cannot reference a field before it's defined.
But the line System.out.println(this.test);
is not
Why does this not error when I qualify it?
回答1:
As with so many of these questions, it's because the JLS says so.
8.3.2.3 Restrictions on the use of Fields during Initialization
The declaration of a member needs to appear textually before it is used only if the member is an instance (respectively
static
) field of a class or interface C and all of the following conditions hold:
- The usage occurs in an instance (respectively
static
) variable initializer of C or in an instance (respectivelystatic
) initializer of C.- The usage is not on the left hand side of an assignment.
- The usage is via a simple name.
- C is the innermost class or interface enclosing the usage.
It is a compile-time error if any of the four requirements above are not met.
In your (failing) example, the "simple name" case is the condition not met. Qualifying the usage (with this
) is the loophole that resolves the compile-time error.
Explained another way:
The use of fields inside an initialization block before the line on which they are declared can only be on the left hand side of an expression (i.e. an assignment), unless they are qualified (in your case
this.test
).
(paraphrased to suit this question more closely)
回答2:
Because capturing this
is possible at the time that you create the lambda. Capturing test
is not (it is not yet defined). Later though, inside the lambda you can access test
(via this
).
来源:https://stackoverflow.com/questions/30959458/cannot-reference-a-field-before-it-is-defined-but-only-if-you-dont-qualify-it