Win a copy of The Business Blockchain this week in the Cloud forum!
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic

Difference between declaring and initializing a variable

 
Jan Stückrath
Greenhorn
Posts: 24
2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I am currently reading the OCA Java SE 8 Programmer I Study Guide. On page 20 it says "You can't refer to a variable before it has been initialized". This is obiously true, since otherwise the value of the variable would be undefined, but I think this condition is not strong enough. However, this depends on what "initialize" means.

The following class compiles and i is initialized with the value 10:

This class will not compile, since i is used without being initialized:

However, this class will also not compile, although i has a fixed value after line 2:

In the last example I assumed, that i is initialized after it is assigned the value 10. But it can still not be referred to until after its declaration. Is this a general rule, or am I just assuming?
 
Joe Bishara
Ranch Hand
Posts: 175
17
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
This section of the JLS explains the conditions under which the declaration of a member must appear textually before it is used.

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 (respectively static) 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.


  • This code compiles because the declaration of nickname appears textually before it is used in the instance variable initializer

    This code compiles because the declaration of nickname appears textually before it is used in the instance initializer
     
    Jan Stückrath
    Greenhorn
    Posts: 24
    2
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Thank you, so it is a general rule. The specification you linked explains it, but is there als an intuition why they defined it this way?
    I find it especially odd in the static case, where I could replace the right side of an assignment with a function call to make to code valid.
    What is the reason why they want this code not to compile:

    But defined this code to be correct?
     
    Joe Bishara
    Ranch Hand
    Posts: 175
    17
    • Likes 1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    In your code, the static initializer doesn't use the declared staticint field. It uses a copy of the declared staticint field. Whenever a method returns a primitive field, it returns a copy.

    Jan Stückrath wrote:...but is there als an intuition why they defined it this way?

    I think that because the compiler reorganizes your source code in a certain order, there are certain rules that must be adhered to.

    The compiler copies code in the textual order in which they appear:
  • static initializers will be copied into a class initialization method (generated by the compiler) in the textual order in which they appear in source code
  • instance initializers will be copied into every constructor in the textual order in which they appear in source code

  • The JLS explains the execution of class and interface initializers as follows:
    ...execute either the class variable initializers and static initializers of the class, or the field initializers of the interface, in textual order, as though they were a single block.

    The JLS explains the execution of instance initializers as follows:
    ...execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class.

    When I compile this source code

    The JAD decompiler's interpretation of the bytecode is
     
    Jan Stückrath
    Greenhorn
    Posts: 24
    2
    • Likes 1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Joe Bishara wrote:In your code, the static initializer doesn't use the declared staticint field. It uses a copy of the declared staticint field.

    I am not sure this is entirely true. My working example above initializes staticint to 20, so the first line of the static initializer block sets the value to 10, which is then increased by another 10. All this changes are performed on the "original" staticint, otherwise they would not add up to 20.
    I also constructed the following example using a non-primitic class variable:

    It compiles and outputs "10", so staticint is initialized to a valid Store object.

    How the compiler handles these blocks is interesting. Shouldn't the compiler generate this correct code:

    from one of my initial non-compiling examples?

     
    Joe Bishara
    Ranch Hand
    Posts: 175
    17
    • Likes 3
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    The rule is simple. If a field is used by an initializer, the declaration of the field must appear textually before it is used. This rule does not apply if a copy of a field is used by an initializer.

    In other words, if an initializer directly accesses a field, the field must appear textually before it is used. This rule does not apply if an initializer directly accesses a copy of a field.

    In your Initialization2 class, the staticint field is not used by the static initializer; it is used by the add() method i.e. it is directly accessed by the add() method.

    On a side note, remember that:
  • a method that returns a primitive type field returns a copy of the field
  • a method that returns a reference type field also returns a copy of the field

  • The difference is that a copy of a reference type field can still access the original object (if it is not null).

    Jan Stückrath wrote:Shouldn't the compiler generate this correct code...

    All we can do as developers is follow the rules specified in the JLS.
     
    Ado Di
    Greenhorn
    Posts: 2
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Joe Bishara, I found your explanation about the JAD decompiler's interpretation of the byte code revealing.

    However, the code below does not compile because I cannot reference a field before it is defined;



    But if the bytecode looks like this below, why the code above does not compile. Because the code below compiles.




     
    Joe Bishara
    Ranch Hand
    Posts: 175
    17
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Hello Ado

    Welcome to the ranch. You've provided a reason why your code does not compile. Like I said earlier, all we can do as developers is follow the JLS rules and ensure that the declaration of a field appears textually before it is used in an initializer.
     
    Ramya Subraamanian
    Ranch Hand
    Posts: 178
    17
    • Likes 1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Jan,
    your code snippets from your initial post don't compile because you are making an "illegal forward reference" of the instance variable.
    In your static variable example also:

    http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.3.3 -has information about Forward References During Field Initialization and also some examples similar to your code snippet.

    my assumption is you can’t reference a instance variable before it is defined but we can initialize a instance variable before it is defined.

    Good explanation Joe.
     
    Jan Stückrath
    Greenhorn
    Posts: 24
    2
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Joe Bishara wrote:All we can do as developers is follow the rules specified in the JLS.

    I feared you would say that. Thank you for explaining the general rule and linking the specification. I was just wondering about the reason why they specified it this way. From my experience, "strange" behavior of a language is often caused by compatibility issues with other features or similar problem. In this case I could just not see the reason for the specification to be this way.
     
    Joe Bishara
    Ranch Hand
    Posts: 175
    17
    • Likes 1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Jan Stückrath wrote:From my experience, "strange" behavior of a language is often caused by compatibility issues with other features or similar problem. In this case I could just not see the reason for the specification to be this way.

    I don't see it as "strange" behaviour. The compiler is your friend. It will not catch every bug, but it will take every precaution to prevent what it perceives to be a potential bug. One of these potential bugs is using a field’s default value of 0 or null even though you have explicitly initialized the field to a valid non-default value.

    As mentioned earlier, the compiler organizes your initializers in the textual order in which they appear.

    So this source code

    generates this bytecode

    If this source code was allowed

    the generated bytecode will be

    The potential bug here is that the initialization of x uses the default value of y (i.e. 0) even though y has a non-default value (i.e. 9).

    The compiler will not always stop you from writing these potential bugs. For example, this source code compiles

    and generates this bytecode
    As you can see, it's not impossible to write these potential bugs in your code. You can even "deceive" the compiler by using a copy as you did earlier. But as much as possible, the compiler will do its best to prevent what it perceives to be a potential bug.

     
    Jan Stückrath
    Greenhorn
    Posts: 24
    2
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Joe Bishara wrote:
    I don't see it as "strange" behaviour. The compiler is your friend. It will not catch every bug, but it will take every precaution to prevent what it perceives to be a potential bug.

    What I still find strange is not that they forbid some of the code, but the line they draw between correct and invalid code. It completely makes sense that they do not allow something like this:

    It tricks the programmer into thinking x is first set to 5 and then incremented. However, they could also be more strict and forbid the usage of x, also on the left side, before its delcaration. They seem to see some kind of benefit in the possibility to write this:

    But then again, they do not allow:

    By specification the lines are executed in order, so x has a fixed value after the first line of the initializer block and the second line should not be a problem. When x is set to 5 any previous value of x is discarded anyway. If we define a variable in a local scope, then the compiler will warn us if we forgot to initialize it and also analyses control structures to some extent, so they could have specified similar checks in this case.
    But don't get me wrong, I want the compiler to help me prevent mistakes. And it already helped me multiple times. I am just curious about why some things are allowed and some are not. I know from my experiences with C++ that even unintuitive behaviour (which is of course subjective) often has its reasons, even if they are somtimes just "it was easier to specify".
     
    Joe Bishara
    Ranch Hand
    Posts: 175
    17
    • Likes 1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    I’m sure that you’re not the only one with ideas about “improvements” that can be made to the compiler. Some people think that the compiler is too strict in some areas and some think that it is not strict enough in other areas. The compiler can only do so much.

    These days, many companies use specialized static analysis tools to minimize potential bugs and improve the quality of source code because they are not satisfied with the static analysis done by the compiler to generate compiler warnings.
     
    nick woodward
    Ranch Hand
    Posts: 370
    11
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Not wanting to hijack the thread, but I also find this confusing - especially that the instance initialiser blocks and statements are executed in order, but that an assignment statement is allowed prior to declaration, but access is not (although the latter definitely makes sense). The compiler must surely know of the variables in advance in order to allow assignment, no? Maybe when 'new' is called or a class is loaded or something?

    Also, wow, the JLS can be so poorly written at times! 'respectively static'?
     
    Jan Stückrath
    Greenhorn
    Posts: 24
    2
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Joe Bishara wrote:The compiler can only do so much.

    Well, everything has its limits. We just need a feature that allowes the developers to configure their compilers behaviour. What could possible go wrong?

    @Nick
    That was also the behaviour that confused me in the beginning, but it is clearly specified this way. For now I assume that they cosidered a less (or more) strict specification not worth the effort.
     
    nick woodward
    Ranch Hand
    Posts: 370
    11
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    @Nick
    That was also the behaviour that confused me in the beginning, but it is clearly specified this way. For now I assume that they cosidered a less (or more) strict specification not worth the effort.


    ah ok, i did read the thread but wasn't 100%

    anyway, regardless of it being specified, it still confuses me as to *how* the compiler can allow it if it reads sequentially - unless it is already aware that the variable exists

    for example, why is this:



    allowed, but this is not:



    or, in other words, if the compiler reads sequentially, at what point does it complain about the lack of the variable x? it can't be at 'x = 8', because it reads sequentially and the variable could, for all it knows, come after. So is it at the end of the source file? Or does the compiler first look at variable names?

    hope that makes sense! it probably wont - i had a good holiday

    Nick

     
    Joe Bishara
    Ranch Hand
    Posts: 175
    17
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    nick woodward wrote:...why is this:



    allowed, but this is not:


    The initializers will be executed in the textual order in which they appear:
  • class initializers will be executed in the class initialization method (generated by the compiler)
  • instance initializers will be executed in the constructors

  • Field declaration is a different thing. Before a field can be initialized, it must first be declared.
     
    nick woodward
    Ranch Hand
    Posts: 370
    11
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Joe Bishara wrote:
    nick woodward wrote:...why is this:



    allowed, but this is not:


    The initializers will be executed in the textual order in which they appear:
  • class initializers will be executed in the class initialization method (generated by the compiler)
  • instance initializers will be executed in the constructors

  • Field declaration is a different thing. Before a field can be initialized, it must first be declared.


    and this is where my confusion is coming in - because if initializer blocks and statements are executed inline, then the declaration part of say 'int x = 8' must happen prior to both, correct?

    so in effect when 'new' is called on an object:

    instance variables are created (or allocated) with default values
    instance initializers and statements are called in order


    thanks,

    nick
     
    Joe Bishara
    Ranch Hand
    Posts: 175
    17
    • Likes 1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    In a class, declaration code is separate from initialization code:
  • all instance field initialization code exist in the constructor
  • all instance field declaration code exist outside the constructor

  • When the compiler encounters an instance field declaration with initialization int x = 1, the compiler will move the instance field initialization code x = 1 (not the instance field declaration code int x) to the constructor. The compiler will also move instance initializers (aka instance initialization blocks) to the constructor. They will be moved to the constructor in the order in which they appear in the source code.

    So this code

    will look like this after compilation

    When an instance of Test is created with the new keyword:
  • memory will be allocated to the object
  • the JVM will initialize its fields to default values - at this point, the declaration int x has a value of 0
  • the JVM invoke the object’s constructor to initialize its fields - at this point, the declaration int x has a value of 1
  •  
    nick woodward
    Ranch Hand
    Posts: 370
    11
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Joe Bishara wrote:In a class, declaration code is separate from initialization code:
  • all instance field initialization code exist in the constructor
  • all instance field declaration code exist outside the constructor

  • When the compiler encounters an instance field declaration with initialization int x = 1, the compiler will move the instance field initialization code x = 1 (not the instance field declaration code int x) to the constructor. The compiler will also move instance initializers (aka instance initialization blocks) to the constructor. They will be moved to the constructor in the order in which they appear in the source code.

    So this code

    will look like this after compilation

    When an instance of Test is created with the new keyword:
  • memory will be allocated to the object
  • the JVM will initialize its fields to default values - at this point, the declaration int x has a value of 0
  • the JVM invoke the object’s constructor to initialize its fields - at this point, the declaration int x has a value of 1


  • interesting stuff, i did not know it did that, thanks!

    i'm assuming the initialization code is moved to the beginning of the constructor, right? (after super() which will already have been called)

    thanks for your help!

    nick


     
    Joe Bishara
    Ranch Hand
    Posts: 175
    17
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    nick woodward wrote:i'm assuming the initialization code is moved to the beginning of the constructor, right? (after super() which will already have been called)

    That's right! The first statement in a constructor must be either code that calls another constructor or code that calls a superclass constructor. If you don't add this code yourself, the compiler will implicitly add code that calls the default superclass constructor.

    nick woodward wrote:thanks for your help!

    Glad I could help.
     
    • Post Reply
    • Bookmark Topic Watch Topic
    • New Topic