Why Are My Fields Initialized to Null or to the Default Value of Zero When I'Ve Declared and Initialized Them in My Class' Constructor

Why are my fields initialized to null or to the default value of zero when I've declared and initialized them in my class' constructor?

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.

Why is my object variable considered null?

Because you have shadowed the fields by declaring local variables of the same name, that immediately leave scope. This,

private void generateNumbers(){
RandNum first = new RandNum(min, max);
RandNum second = new RandNum(min, max);
}

should be

private void generateNumbers(){
this.first = new RandNum(min, max);
this.second = new RandNum(min, max);
}

Why am I getting null as a value of array?

You are shadowing a variable:

public Hra1()
{
// the following variable's *scope* is inside of this constructor only
// outside of the constructor, the local variable below doesn't exist.
Mince [] poleMinci = new Mince[20];

poleMinci[0] = new Mince("stříbrná", "coin.png");
poleMinci[3] = new Mince("stříbrná", "coin.png");
poleMinci[4] = new Mince("zlatá", "coin_gold.png");
poleMinci[8] = new Mince("stříbrná", "coin.png");
poleMinci[10] = new Mince("stříbrná", "coin.png");
poleMinci[12] = new Mince("stříbrná", "coin.png");
}

In that constructor, since poleMinci was declared inside of the constructor, it is visible only inside of the constructor. If you have a variable of the same name in the class it will be null. To fix this, don't re-declare the variable locally. Do:

public Hra1()
{

// Mince [] poleMinci = new Mince[20]; // **** not this ****
poleMinci = new Mince[20]; // **** but this. note the difference? ****

poleMinci[0] = new Mince("stříbrná", "coin.png");
poleMinci[3] = new Mince("stříbrná", "coin.png");
poleMinci[4] = new Mince("zlatá", "coin_gold.png");
poleMinci[8] = new Mince("stříbrná", "coin.png");
poleMinci[10] = new Mince("stříbrná", "coin.png");
poleMinci[12] = new Mince("stříbrná", "coin.png");
}

For more on this problem, check out Variable Shadowing. Most IDE's either will warn you that you might be doing this, or have a setting that will allow them to do this. I use Eclipse and have set my IDE to warn me. You might wish to do this too.

java.lang.NullPointerException Error

You're shadowing the healthBenDesig by re-declaring it in your constructors leaving the class field null. Don't re-declare it.

Change

public VariableList() {
HashMap<String, Double> healthBenDesig = new HashMap<String, Double>();
}

to:

public VariableList() {
healthBenDesig = new HashMap<String, Double>();
}

Initialize class fields in constructor or at declaration?

My rules:

  1. Don't initialize with the default values in declaration (null, false, 0, 0.0…).
  2. Prefer initialization in declaration if you don't have a constructor parameter that changes the value of the field.
  3. If the value of the field changes because of a constructor parameter put the initialization in the constructors.
  4. Be consistent in your practice (the most important rule).


Related Topics



Leave a reply



Submit