Granny's Programming Pearls
"inside of every large program is a small program struggling to get out"
JavaRanch.com/granny.jsp
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Liutauras Vilda
  • Jeanne Boyarsky
  • Devaka Cooray
  • Paul Clapham
Sheriffs:
  • Tim Cooke
  • Knute Snortum
  • Bear Bibeault
Saloon Keepers:
  • Ron McLeod
  • Tim Moores
  • Stephan van Hulst
  • Piet Souris
  • Ganesh Patekar
Bartenders:
  • Frits Walraven
  • Carey Brown
  • Tim Holloway

instance variable declarations, when do they occur?

 
Ranch Hand
Posts: 179
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi guys.

I just stumbled upon a strange behaviour here and would like to know if someone can explain it, consider the following code:



Now, if you try to run this code it compiles and executes fine. However, if you uncomment the println that attempts to print 'i', you will get a "illegal forward reference" compiler error. Furthermore, adding the reference "this" infront of the 'i' inside the println call solves the problem.

So, how or why is it possible to SET the instance variable using nothing but 'i' to reference it, but when it comes to reading it we are forced to use 'this.i' ?

As a "follwup question", I know that instance initialization blocks run right after the super-class constructor has run, but when exactly do the instance variable declarations take place?

super init blocks
super constructor
child init blocks
child constructor

Not including static initialization blocks, where in the above code do the instance variable declarations take place? It seems it would be right after the super constructor, before the instance initialization block, but that's just my guess.

PS: I just noticed that if the declaration line includes an initialization value (int i = 10), then the previously assigned value inside the init block will be overridden, implicating the delcaration has not taken place before the init block. But how would that be possible when we can reference the variable inside the block?

// Andreas
 
Greenhorn
Posts: 21
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hey, Andreas.

It is not unusual. It is just a matter of forward referencing.
Forward referencing means reading a variable before declaring it, and is illegal. Why? To prevent circular assignments like:



It is just a restriction, it is NOT as if the program doesn't know that there is a variable because it didn't reach the line of the declaration.
That is why you can use the variable on the left side of an assignment. And that is why you can refer to this.i (which will be considered 0, no matter what you expect).
Even this is legal:


For more information, go to Java Language Specifications third edition - 8.3.2.3 Restrictions on the use of Fields during Initialization.
JLS almost always has the answer. It is just sometimes hard to find it

Best wishes,
Vali
 
Andreas Svenkson
Ranch Hand
Posts: 179
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I still think this is a bit controversial though...

I read the rules in the language specs, and sure if those are the rules then "ok", I mean they don't make 100% sense if you ask me (like why the ref variable name has to be "short").

But the conclusions I gather from this is...

1: you can read and write to instance variables before or after their textual declaration, but if your declarations have an initialization value in them they will override any value previously given to them (like in an initializer block)
2: if you want to READ an instance variable, textually before its declaration, then you MUST use this.<var>, even though you can just use <var> if you want to write to it.
3: both above rules apply to static variables as well (that's the way I get it alteast?)
4: basically all variable names are known before anything else gets executed, as in we can read/write to instance variables from the get-to, textually ahead of their declaration or not, but their textual declaration order still matters and will overwrite any previous value assigned to them.

5: the reason your code:

int i = j = 4;
int j;


.. works is because of the fact that the variable j gets an assigned value*before trying to read from it? Otherwise you would have needed to write i=this.j am I right?

// Andreas

Please feel free to add any comments to my above conclusions...

// Andreas
 
Andreas Svenkson
Ranch Hand
Posts: 179
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I'm writing another post about this, simply cus the ambiguity of the issue won't leave my brain alone Take for instance what you said...

Valentin Musoiu wrote:Hey, Andreas.

It is not unusual. It is just a matter of forward referencing.
Forward referencing means reading a variable before declaring it, and is illegal. Why? To prevent circular assignments like:





Now, obviously this type of circular assigment is not a good thing... but if the point of this restriction is to prevent it, then why is it still be doable if you reference the variables using this.<var>, or <Class>.var for static vars, like so:



This works.... and it sortof annoys me that it does

// Andreas
 
Valentin Musoiu
Greenhorn
Posts: 21
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi, Andreas.

I'm so sorry for this late reply. Been awfully sick and then I forgot about my post

This time I cannot but state my own opinion on this matter.

I think restrictions are introduced in a language to help developers with errors that can easily be done, not to make the language idiot-proof.

In my opinion it is easy to make a mistake like the first (especially if there is a lot of code between the 2 statements).
As for the latter, if you put "this." in front of the variables, then you probably are aware of the implications of your actions. Although I still cannot find a valid reason why would you want to do that...

Once again, this is only my opinion. For more details, you will probably have to contact Gosling himself
 
Andreas Svenkson
Ranch Hand
Posts: 179
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Valentin Musoiu wrote:Hi, Andreas.

I'm so sorry for this late reply. Been awfully sick and then I forgot about my post

This time I cannot but state my own opinion on this matter.

I think restrictions are introduced in a language to help developers with errors that can easily be done, not to make the language idiot-proof.

In my opinion it is easy to make a mistake like the first (especially if there is a lot of code between the 2 statements).
As for the latter, if you put "this." in front of the variables, then you probably are aware of the implications of your actions. Although I still cannot find a valid reason why would you want to do that...

Once again, this is only my opinion. For more details, you will probably have to contact Gosling himself



Hi again Valentin, and thanks for responding.

Well I'm sure it doesn't come as a surprise to you when I say that I would never do this type of circular assignment.... But just like many other things concerning taking the SCJP, one is forced to question many small details that might (or might not) appear on the exam, in case you you get asked about it

However, the bigger question here for me personally, is to determine once and for all *exactly* in what order things happen when a class is loaded and/or instantiated, something I am pretty sure about but have been forced to question due to these "weird" rules concerning the instance variables.

Here's how I believe it works though...

1: During class loading the first thing that happens is that all class variables are assigned their *default value*, as is specified by the reference variable type (integers=0, objects=null, etc...)

2: The static initializers are then executed top-to-bottom, together with the static variable declarations. If the variable declarations include an initialization value, that value is assigned, overriding the previously assigned value; be it the default value, or any value assigned to the variable in a static initialization block textually prior to the variable declaration (like the cases we've discussed in this thread).
- If the declaration does NOT include an initialization value, the latest assigned value (either the default value or, if it exists, the value given in a previously executed static initializer) is retained after the variable declaration.

3: If the class in question has superclasses other than Object, points 1-2 will occur in each of those classes before they occur in the "subject class", starting in the top-most superclass and working its way downward.

4: During instantiation; Points 1-2 will occur for instance variables and instance initialization blocks, starting with the topmost superclass. The rules are the same regarding reading/writing to instance variables textually prior to their declaration, retaining the most recently assigned value.

5: The constructors are then run from top-to-bottom in each superclass, and finally the "subject class".
(To be 100% correct here, I believe that the constructor actually performs its call to super() BEFORE the initialization blocks / variable declarations occur. However the body of the constructor is not run til AFTER these have taken place, so in practice I think it's ok to phrase it like I did.)

So for me personally, the most important realization that comes from this is that AS SOON as an object exists, or a class is loaded, you IMMEDIATELY have access to all variables in it. We can write to them without problems, and read from them too (when explicity using this.<var> or <Class>.<var>, IF we are reading before their textual declaration).

I know I've written quite a bit here, hopefully you have the time to read through it because like I said this is how I *believe* it works (I'm pretty sure though ), and as such I would appreciate it if someone could confirm

// Andreas

 
Ranch Hand
Posts: 448
Eclipse IDE Firefox Browser Tomcat Server
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
One can verify the above facts using a decompiler to see who java compiler analyzes our code.
 
Andreas Svenkson
Ranch Hand
Posts: 179
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks for the tip, I haven't gotten around to using a decompiler yet but I've thought about it and heard JDebug is pretty good? Gonna look into that.

// Andreas
 
Greenhorn
Posts: 27
Eclipse IDE Java Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi Andreas, you said

2: The static initializers are then executed top-to-bottom, together with the static variable declarations. If the variable declarations include an initialization value, that value is assigned, overriding the previously assigned value; be it the default value, or any value assigned to the variable in a static initialization block textually prior to the variable declaration (like the cases we've discussed in this thread).
- If the declaration does NOT include an initialization value, the latest assigned value (either the default value or, if it exists, the value given in a previously executed static initializer) is retained after the variable declaration.



I think you are wrong about this topic. The latest value assigned to a member seems to be the init block one (if exists). If not, the assigned value will be the one that appears in the member declaration (or the default value, of there is no assigned value). You can easily check what I say by running this code:

 
Francisco J. Bermejo
Greenhorn
Posts: 27
Eclipse IDE Java Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

I think you are wrong about this topic. The latest value assigned to a member seems to be the init block one (if exists). If not, the assigned value will be the one that appears in the member declaration (or the default value, of there is no assigned value). You can easily check what I say by running this code:



It turns out that I'm wrong too . It seems that the latest value assigned is the latest in the source code !! If you change my code to this:


The output will be "6 7"!!! But if you don't assign any value in the member declaration, then the assigned value is the init block one (no matter it is typed before or after the member declaration).
 
Andreas Svenkson
Ranch Hand
Posts: 179
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi Francisco.

Yes, I think I know what you are talking about, and I sortof screwed up with my phrasing there, I somehow "forgot" to include the case where you might have an initialization block after the variable declaration.

But the point is, like you say, whichever statement that is textually the last one to change a variable will be the one that matters, and whose value is retained in the variable. Crudely put, one might think of the initializers and the variable declarations as being part of the same "method" or init block. Whichever statement comes last, can nullify or modify any previous assignments made to the variable earlier.

Here's another interesting parenthesis, we can even reference the variable itself in its declaration initialization, thus modifying its value by originating from the default assigned value.(this code also shows pretty clearly what I mean in my sentence above).



Here's another interesting piece....



These examples are sortof pointless in terms of creating productive code and I doubt I will ever find it useful, but the fact that we can write code like this is still interesting...

// Andreas
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!