class Test {
int a;
void method() {
a = 1;
int a = a = 2;
a = 3;
}
}
There are lots of as in method. What are they all referring to?
This is a simple example of the bizarreness of Java's scoping rules.
a = 1;
int a = a = 2;
a = 3;
Breaking it down line-by-line:
a = 1; is referring to the member variable.
a = 3; is referring to the local variable, because it's after the declaration of the local variable. It's pretty confusing that you can refer to two different symbols via the same identifier, in the same method.
int a = a = 2;: the second a is the local variable.
The self-reference in the variable declaration is really curious. You can find this in the language spec:
The scope of a local variable declaration in a block (§14.4) is the rest of the block in which the declaration appears, starting with its own initializer and including any further declarators to the right in the local variable declaration statement.
It is also true that member variables can refer to themselves in their own initializer; but this is for a slightly different reason:
The scope of a declaration of a member m declared in or inherited by a class type C (§8.1.6) is the entire body of C, including any nested type declarations.
I have yet to find a compelling reason for the existence of this rule for local variables; maybe you just really really want to make sure it's assigned. The only thing that I can think it allows you to do is to use the variable as temporary storage in the evaluation of the initializer, for example:
int a = method(a = somethingThatIsReallyExpensiveToCompute(), a);
would be equivalent to:
int a;
{
int tmp = somethingThatIsReallyExpensiveToCompute();
a = method(tmp, tmp);
}
Personally, I'd rather see the second form of the code, as the evaluation of the first just seems obscure.
Related
Why don't we initialize the fields as soon as we declare them in the class? Could the workflow of the code differ if we have initialized the fields as soon as we have declared them? The point is, why we don't initialize them above the constructor?
First of all: we can!
For a more in-depth answer, I will quote The Java Tutorials that says:
However, this form of initialization has limitations because of its simplicity. If initialization requires some logic (for example, error handling or a for loop to fill a complex array), simple assignment is inadequate.
Therefore, to keep a coherent and homogeneous way of writing the code that ensures the code readability, we tend to avoid initializing some fields directly and some others in the constructor (or elsewhere).
We can initialize a field when it is declared:
class Foo {
int a = 1;
}
We can initialize a field in a constructor:
class Foo {
int a;
Foo() {
a = 1;
}
}
We can even initialize a field in an initializer block:
class Foo {
int a;
{
a = 1;
}
}
Actually, the compiler converts the first and third forms into the second.
(Of course, if the field isn't final, we can also (re)assign it in a method).
If you know the initial value a needs to have when you're writing the code, and it's a simple expression, by all means write it in the first way; it's generally worth avoiding the initializer way (mostly because it's unusual, and not very pretty). And this way is great, if you have multiple constructors, but want to set the same value in all the constructors: the same initialization gets copied into all of the constructors (or, at least, the ones which don't invoke this(...)).
But if you need to set a based on something like a constructor parameter, you have to do (something like) the second way:
class Foo {
int a;
Foo(int a) {
this.a = a;
}
}
Which of the approaches you use depends on the situation.
I only initialize final fields, everything else is initialized by constructors.
class MyClass{
final static int SERIAL = 12345678;
int a, b, c, d;
MyClass(int i){
a = b = c = d = i;
}
MyClass(){
this(1);
}
}
I've checked Forward References During Field Initialization and this the answer from #assylias, but still I got no answer to the why.
Why a static block can assign the static variable declared after it but can NOT access it?
class Parent {
static {
i = 2; // valid
// can only assign new value to it instead of accessing it?
// System.out.println(i); // invalid - compile-error
}
static int i = 0;
static {
i = 3; // valid
}
}
Is it due to the fact: the value is not initialized yet, so we just explicitly inhibit you from using it? or there are something related to security I don't know?
updated
this is not a duplicate of that problem which is about
Why this doesn't happen when accessing with class name?
This question is about why we have this design? for what purpose?
Static fields are initialized based on the order they appear in the code.
So, when you assign a value to i variable you just say to the compiler: "Hey, guy, when you get to initialize this variable, set its value to...". But you can not use it until it's initialized because it simply does not exist yet.
UPDATE:
As it says in the book "The Java Language Specification" by James Gosling, Bill Joy, Guy Steele and Gilad Bracha:
These restrictions are designed to catch, at compile time, circular or
otherwise malformed initializations.
Consider this:
static {
i = 2;
j = i + 5; //should it be 7 or 15?
}
static int i = 10;
static int j;
Should j variable be 7 or 15?
If it's 7, then we have initialized i variable twice, which is not possible, since the field is static. If it's 15 then what does i = 2; mean?
This code is ambiguous, so Java Specification does not allow to do that.
After some further reading, I think Pavel is not quite accurate in this point as #Holger pointed out in the comment.
we have initialized i variable twice, which is not possible, since the field is static.
As the 12.4.2. Detailed Initialization Procedure points out
For each class or interface C, there is a unique initialization lock LC. The mapping from C to LC is left to the discretion of the Java Virtual Machine implementation.
I suppose the initialization twice is okay to the class initializer itself as long as it's just once to the invoking clients.
But the demos Pavel provided still stands its position so I basically just reuse it here but with different explanation.
static {
i = 2;
j = i + 5;
// no one knows whether "i" here initialized properly here
}
static int i = 10;
static int j;
But when you use MyClass.i directly in j = MyClass.i + 5, the compiler would know it would then be okay as 8.3.3. Forward References During Field Initialization detailed with four conditions.
Specifically, it is a compile-time error if all of the following are
true:
The declaration of a class variable in a class or interface C appears
textually after a use of the class variable;
The use is a simple name in either a class variable initializer of C
or a static initializer of C;
The use is not on the left hand side of an assignment;
C is the innermost class or interface enclosing the use.
And there is a detailed discussion in this answer already.
To wind it up, I think this would be for predictable behavior to add these restrictions. Once again, the other official purpose pointed out in 8.3.3. Forward References During Field Initialization.
These restrictions are designed to catch, at compile time, circular or otherwise malformed initializations.
The same is also applicable to the non-static members. You can assign values to them in instance initializer block but can not use them before initialization.
class Parent {
{
x = 3; // works fine
// System.out.println(x); // gives compilation error.
}
int x = 0;
public static void main(String[] args) {
}
}
This is meant to be a canonical question and answer for similar questions where the issue is a result of shadowing.
I've defined two fields in my class, one of a reference type and one of a primitive type. In the class' constructor, I try to initialize them to some custom values.
When I later query for those fields' values, they come back with Java's default values for them, null for the reference type and 0 for the primitive type. Why is this happening?
Here's a reproducible example:
public class Sample {
public static void main(String[] args) throws Exception {
StringArray array = new StringArray();
System.out.println(array.getCapacity()); // prints 0
System.out.println(array.getElements()); // prints null
}
}
class StringArray {
private String[] elements;
private int capacity;
public StringArray() {
int capacity = 10;
String[] elements;
elements = new String[capacity];
}
public int getCapacity() {
return capacity;
}
public String[] getElements() {
return elements;
}
}
I expected getCapacity() to return the value 10 and getElements() to return a properly initialized array instance.
Entities (packages, types, methods, variables, etc.) defined in a Java program have names. These are used to refer to those entities in other parts of a program.
The Java language defines a scope for each name
The scope of a declaration is the region of the program within which
the entity declared by the declaration can be referred to using a
simple name, provided it is visible (§6.4.1).
In other words, scope is a compile time concept that determines where a name can be used to refer to some program entity.
The program you've posted has multiple declarations. Let's start with
private String[] elements;
private int capacity;
These are field declarations, also called instance variables, ie. a type of member declared in a class body. The Java Language Specification states
The scope of a declaration of a member m declared in or inherited by a
class type C (§8.1.6) is the entire body of C, including any nested
type declarations.
This means you can use the names elements and capacity within the body of StringArray to refer to those fields.
The two first statements in your constructor body
public StringArray() {
int capacity = 10;
String[] elements;
elements = new String[capacity];
}
are actually local variable declaration statements
A local variable declaration statement declares one or more local variable names.
Those two statements introduce two new names in your program. It just so happens that those names are the same as your fields'. In your example, the local variable declaration for capacity also contains an initializer which initializes that local variable, not the field of the same name. Your field named capacity is initialized to the default value for its type, ie. the value 0.
The case for elements is a little different. The local variable declaration statement introduces a new name, but what about the assignment expression?
elements = new String[capacity];
What entity is elements referring to?
The rules of scope state
The scope of a local variable declaration in a block (§14.4) is the
rest of the block in which the declaration appears, starting with its
own initializer and including any further declarators to the right in
the local variable declaration statement.
The block, in this case, is the constructor body. But the constructor body is part of the body of StringArray, which means field names are also in scope. So how does Java determine what you're referring to?
Java introduces the concept of Shadowing to disambiguate.
Some declarations may be shadowed in part of their scope by another
declaration of the same name, in which case a simple name cannot be
used to refer to the declared entity.
(a simple name being a single identifier, eg. elements.)
The documentation also states
A declaration d of a local variable or exception parameter named n
shadows, throughout the scope of d, (a) the declarations of any other
fields named n that are in scope at the point where d occurs, and (b)
the declarations of any other variables named n that are in scope at
the point where d occurs but are not declared in the innermost class
in which d is declared.
This means that the local variable named elements takes priority over the field named elements. The expression
elements = new String[capacity];
is therefore initializing the local variable, not the field. The field is initialized to the default value for its type, ie. the value null.
Inside your methods getCapacity and getElements, the names you use in the in their respective return statements refer to the fields since their declarations are the only ones in scope at that particular point in the program. Since the fields were initialized to 0 and null, those are the values returned.
The solution is to get rid of the local variable declarations altogether and therefore have the names refer to the instance variables, as you originally wanted. For example
public StringArray() {
capacity = 10;
elements = new String[capacity];
}
Shadowing with constructor parameters
Similar to the situation described above, you may have formal (constructor or method) parameters shadowing fields with the same name. For example
public StringArray(int capacity) {
capacity = 10;
}
Shadowing rules state
A declaration d of a field or formal parameter named n shadows,
throughout the scope of d, the declarations of any other variables
named n that are in scope at the point where d occurs.
In the example above, the declaration of the constructor parameter capacity shadows the declaration of the instance variable also named capacity. It's therefore impossible to refer to the instance variable with its simple name. In such cases, we need to refer to it with its qualified name.
A qualified name consists of a name, a "." token, and an identifier.
In this case, we can use the primary expression this as part of a field access expression to refer to the instance variable. For example
public StringArray(int capacity) {
this.capacity = 10; // to initialize the field with the value 10
// or
this.capacity = capacity; // to initialize the field with the value of the constructor argument
}
There are Shadowing rules for every kind of variable, method, and type.
My recommendation is that you use unique names wherever possible so as to avoid the behavior altogether.
int capacity = 10; in your constructor is declaring an local variable capacity which shadows the field of the class.
The remedy is to drop the int:
capacity = 10;
This will change the field value. Ditto for the other field in the class.
Didn't your IDE warn you of this shadowing?
There are two parts to using variables in java/c/c++. One is to declare the variable and the other is to use the variable (whether assigning a value or using it in a calculation).
When you declare a variable you must declare its type. So you would use
int x; // to declare the variable
x = 7; // to set its value
You do not have to re-declare a variable when using it:
int x;
int x = 7;
if the variable is in the same scope you will get a compiler error; however, as you are finding out, if the variable is in a different scope you will mask the first declaration.
Another widely accepted convention is to have some prefix (or suffix - whatever you prefer) added to class members to distinguish them from local variables.
For example class members with m_ prefix:
class StringArray {
private String[] m_elements;
private int m_capacity;
public StringArray(int capacity) {
m_capacity = capacity;
m_elements = new String[capacity];
}
public int getCapacity() {
return m_capacity;
}
public String[] getElements() {
return m_elements;
}
}
Most IDEs have already available support for this notation, below is for Eclipse
I just have started to learn Java. I have some dummy question. I don't really get why in this situation:
int j = 5;
for (int j = 0; j < 10; j++) {
// do sth
}
my compiler says : the variable j is already defined in the scope.
Why this second j is a problem ? I thought that it should simply shadow the first one.
The problem is that you're declaring the variable j twice: One out of the for loop and one inside. Just delete the line above the for and you'll be good to go.
Local variables aren't shadowed - perhaps you had fields in mind (but that's something different from what you have here).
A simpler yet similar scenario is:
int i = 0;
{
int i = 2;
}
So you have two i variables. Which one do you mean when you reference i ?
The Java compiler doesn't allow 'shadowing' here. The definitions are ambiguous, and the compiler is working to warn you of this.
For the rules for shadowing and obscuring, have a look at the Java Language Specification, Section 6.4
They even provide the same example:
Because a declaration of an identifier as a local variable of a method, constructor, or initializer block must not appear within the scope of a parameter or local variable of the same name, a compile-time error occurs for the following program:
class Test1 {
public static void main(String[] args) {
int i;
for (int i = 0; i < 10; i++)
System.out.println(i);
}
}
This restriction helps to detect some otherwise very obscure bugs. A similar restriction on shadowing of members by local variables was judged impractical, because the addition of a member in a superclass could cause subclasses to have to rename local variables. Related considerations make restrictions on shadowing of local variables by members of nested classes, or on shadowing of local variables by local variables declared within nested classes unattractive as well.
Take it this way. If you were allowed to declare the variable i in the for loop, then how would you refer to the local variable i, declared before the for loop? You can't do that with any qualified name, and the simple name would refer to the for loop variable. That is why it isn't allowed.
The behaviour is as listed in JLS. From JLS - Section 6.4:
A local variable (§14.4), formal parameter (§8.4.1), exception parameter (§14.20), and local class (§14.3) can only be referred to using a simple name (§6.2), not a qualified name (§6.6).
Some declarations are not permitted within the scope of a local variable, formal parameter, exception parameter, or local class declaration because it would be impossible to distinguish between the declared entities using only simple names.
And from JLS - Section 6.3:
The scope of a local variable declaration in a block (§14.4) is the rest of the block in which the declaration appears, starting with its own initializer and including any further declarators to the right in the local variable declaration statement.
Emphasis mine.
Again, in JLS Section 6.4, it is specified that re-declaration of local variable in the same scope will result in compile-time error:
It is a compile-time error if the name of a local variable v is redeclared as a local variable of the directly enclosing method, constructor, or initializer block within the scope of v; or as an exception parameter of a catch clause in a try statement of the directly enclosing method, constructor or initializer block within the scope of v; or as a resource in a try-with-resources statement of the directly enclosing method, constructor or initializer block within the scope of v.
Cause this is a duplicate local variable problem, You already define "j" variable before. Try this:
int i = 5;
for (int j = 0; j < 10; j++) {
// do sth
}
Shadowing only occurs if one of the variables is a method field and the other one is local variable. In your case both are local variables so they cannot shadow each other.
You cannot have two local variables with the same name if they share a scope in the same way you cannot have two fields with the same name.
You have declared j variable twice . Rewrite your code as below :
for (int j = 0; j < 10; j++) {
// do sth
}
or
int j;
for (j = 0; j < 10; j++) {
// do sth
}
int j = 5; // this j is visible to whole method
for (int j = 0; j < 10; j++) {
// so still j is visible to this for loop and you can use it
// but you can't initialize it again
// do sth
}
If I write the following class:
public class Example {
int j;
int k;
public Example(int j, int k) {
j = j;
k = k;
}
public static void main(String[] args) {
Example exm = new Example(1,2);
System.out.println(exm.j);
System.out.println(exm.k);
}
}
The program compiles, but when I run the program, the main method will print out two 0s. I know that in order to say that I want to initialize the instance variables in the constructor I have to write:
this.j = j;
this.k = k;
But if I don't write it, then which variable is evaluated (or considered) in the constructor (on the left and on the write hand side of the expressions)? Is is the argument or the instance variable? Does it make a difference?
Are there other cases where the use of this is obligatory?
If you don't write "this.variable" in your constructor, and if you have a local variable (including the function parameter) with the same name as your field variable in the constructor, then the local variable will be considered; the local variable shadows the field (aka class variable).
One place where "this" is the only way to go:
class OuterClass {
int field;
class InnerClass {
int field;
void modifyOuterClassField()
{
this.field = 10; // Modifies the field variable of "InnerClass"
OuterClass.this.field = 20; // Modifies the field variable of "OuterClass",
// and this weird syntax is the only way.
}
}
}
If you say just j in your constructor then the compiler will think you mean the argument in both cases. So
j = j;
simply assigns the value of the argument j to the argument j (which is a pretty pointless, but nonetheless valid statement).
So to disambiguate this you can prefix this. to make clear that you mean the member variable with the same name.
The other use of this is when you need to pass a reference to the current object to some method, such as this:
someObject.addEventListener(this);
In this example you need to refer to the current object as a whole (instead of just a member of the object).
If you don't write this, then you assign the argument to itself; the argument variables shadow the instance variables.
this is useful when you want to return the object itself
return this;
This is useful because if a class has for example Method1() and Method2(), both returning this, you are allowed to write calls like
object.Method1().Method2()
Also inside a method it can be useful to pass the object itself to another function, during a call.
Another useful approach (though seldom used) is to declare method parameters final:
The following block will not compile, thus alerting you to the error immediately:
public Example(final int j, final int k) {
j = j;
k = k;
}
In your constructor code, you are assigning variables to themselves. 'j' is the j specified in the argument for the constructor. Even if it was the class variable j defined above, then you are still saying "j = j" ... i.e. j isn't going to evaluate differently on the left and on the right.
public Example(int j, int k) {
this.j = j;
this.k = k;
}
What you are experiencing is called variable shadowing. Have a look at this overview for different kinds of variables in Java.
Generally speaking: The Java compiler uses the nearest variable it can find for an assignment. In a method it will first try to find a local variable and then enlarge the focus of its search to class and instance variables.
One habit I personally find good (others don't like it) is prefixing a member variable with m_ and using uppercase for CONSTANT_VARIABLES that don't change their value. Code where variable shadowing is used on purpose is very(!) difficult to debug and work with.
You assign the parameter to itself in your example.
More generally speaking: If you don't prepend a scope to your variable, the current scope is assumed - which is the function in your case. 'this.j' tells the jre to use the variable j within the object scope - the member variable of the object.
Just like Robert Grant said, 'this' is how you make it clear that you are referring to a member variable instead of a local variable.
To answer your follow-up question about this with method, the inner class mentioned by Srikanth is still a valid example of using this: (with method this time)
public class OuterClass
{
void f() {System.out.println("Outer f()");};
class InnerClass {
void f() {
System.out.println("Inner f()");
OuterClass.this.f();
}
}
}
You have the same situation with anonymous class:
You can refer to the outer class’s methods by:
MyOuterClass.this.yOuterInstanceMethod(),
MyOuterClass.myOuterInstanceMethod(),
or simply myOuterInstanceMethod() if there is no ambiguity.
This doesn't quite answer your question, but if you use Eclipse you might find "Assignment has no effect" setting useful. I am sure this will be in other IDEs too.
You might want to take a look at What is the advantage of having this/self pointer mandatory explicit?
Although using this is not mandatory in Java as you noticed I'm sure it will shed some light on the subject of using this in Java as well.
To avoid this, use an IDE (like Eclipse) and it will generate warnings in this case.
Also, make your fields final unless they absolutely can't be. There are a number of reasons (apart from this one) to do that.