Java Forum / General / May 2007
a question related to "final static" field variable
www - 04 May 2007 19:25 GMT Hi,
I want to have a static field variable. I want to initiate it once and don't want it to be changed any more. So
public MyClass { public final static rootDir; //error: saying rootDir may not be initialized
public MyClass(String dir) { rootDir =dir; //error: saying that I cannot assign to a final variable }
...
public void doIt() { ... }
public static void main(String[] args) { String str = args[0]; MyClass class = new MyClass(str);
} }
I thought final variable can be assigned value once and only once. And this can be done inside constructor. Cited from this web page: http://renaud.waldura.com/doc/java/final-keyword.shtml in Final Field part.
Thank you for your help.
Knute Johnson - 04 May 2007 19:34 GMT > Hi, > [quoted text clipped - 32 lines] > > Thank you for your help. Unfortunately not if it is also static.
 Signature Knute Johnson email s/nospam/knute/
Sanjay - 04 May 2007 19:46 GMT > I thought final variable can be assigned value once and only once. And It is a static variable and has nothing to do with the object of that class. It get its value when classloader loads the class. What you are trying to do is, not assigning value to 'rootDir' at all and then are trying to change it in the contructor, hence the error.
> this can be done inside constructor. Cited from this web page: > http://renaud.waldura.com/doc/java/final-keyword.shtml in Final Field part.
I am not sure what that web page says, but there is a subtle difference between final and final static variable. May be you want to read this
http://java.sun.com/docs/books/tutorial/java/javaOO/classvars.html
visionset - 04 May 2007 19:50 GMT > I want to have a static field variable. I want to initiate it once and > don't want it to be changed any more. So [quoted text clipped - 10 lines] > > Thank you for your help. The problem is that the rootDir reference may be accessed before the constructor is called since it is static. The obvious value for it to hold would be null, so you therefore would not be able to reasign in your constructor. Because of this the compiler forces you to assign at the point of declaration.
 Signature Mike W
www - 04 May 2007 20:08 GMT Thank you all. I have realized I was wrong. But, to achieve my purpose, do you have any suggestions?
Thank you.
Patricia Shanahan - 04 May 2007 20:16 GMT > Thank you all. I have realized I was wrong. But, to achieve my purpose, > do you have any suggestions? > > Thank you. Make rootDir private and change it only once inside the class, from main.
If other classes need access you can provide a getRootDir() method, which can check for attempts to read it before it has been set.
Patricia
www - 04 May 2007 21:06 GMT > Make rootDir private and change it only once inside the class, from main. > > If other classes need access you can provide a getRootDir() method, > which can check for attempts to read it before it has been set. > > Patricia Suppose my program has about 20 classes and the class with main method is MyClass provided in my original posting. Suppose one class "Worker.java" needs to know the root directory in order to read in the text file. Right now, Worker does NOT have a MyClass in it. But, if I follow your method, I need: <inside Worker.java> MyClass class = new MyClass(); String rootDir = class.getRootDir(); ...//append rootDir in the front of text file name
</inside Worker.java>
I feel it is kind of silly that in order to get to know what the root directory it is, Worker is forced to have a MyClass in it.
Patricia Shanahan - 04 May 2007 22:04 GMT >> Make rootDir private and change it only once inside the class, from main. >> [quoted text clipped - 17 lines] > I feel it is kind of silly that in order to get to know what the root > directory it is, Worker is forced to have a MyClass in it. Huh? There is no more need to create an object to call a static method than there is to access a static field. If Worker could have accessed MyClass.rootDir, it could call MyClass.getRootDir().
Whether the root directory attribute should be a static attribute of the MyClass class is arguable, but is orthogonal to the question of whether it should be accessed as a variable or through an access method.
Patricia
Eric Sosman - 04 May 2007 22:08 GMT www wrote On 05/04/07 16:06,:
>>Make rootDir private and change it only once inside the class, from main. >> [quoted text clipped - 17 lines] > I feel it is kind of silly that in order to get to know what the root > directory it is, Worker is forced to have a MyClass in it. In the class as you wrote it, the only way MyClass discovers where rootDir is supposed to be is by having somebody, somewhere, create a MyClass object and pass the value in as an argument to the constructor. If you never construct a MyClass, nobody ever tells a constructor what rootDir should be. What should MyClass do? Just make something up out of thin air?
I'm not 100% sure what you're trying to do, but if the idea is for MyClass to "remember" the first rootDir from the construction of the first MyClass instance, then you can't use a static final. Why? Because the static field belongs to the class and not to the instance, and must be initialized when the class is loaded and not later on when instances are (or aren't) constructed.
What you might want to do instead is use a private static variable and an accessor method, something like:
public class MyClass {
private static String rootDir;
public MyClass(String dir) { if (rootDir == null) { // first instance: initialize rootDir = dir; } }
public string getRootDir() { if (rootDir == null) { // optional: could just return null throw new IllegalStateException(...); } return rootDir; } }
 Signature Eric.Sosman@sun.com
Eric Sosman - 04 May 2007 22:28 GMT Eric Sosman wrote On 05/04/07 17:08,:
> www wrote On 05/04/07 16:06,: > [quoted text clipped - 58 lines] > } > } Drat. Make that `public static String getRootDir()', adding `static' and changing `string' to `String', and the weekend had better come soon ...
 Signature Eric.Sosman@sun.com
Knute Johnson - 04 May 2007 22:10 GMT >> Make rootDir private and change it only once inside the class, from main. >> [quoted text clipped - 17 lines] > I feel it is kind of silly that in order to get to know what the root > directory it is, Worker is forced to have a MyClass in it. There are several ways to get your variable to other classes, such as pass them in arguments or use a singleton. In the end it is probably going to be easiest to have a static variable that is visible to your other classes. People are going to shudder though so just don't tell them that you are doing that.
 Signature Knute Johnson email s/nospam/knute/
Mark Space - 05 May 2007 19:28 GMT > I feel it is kind of silly that in order to get to know what the root > directory it is, Worker is forced to have a MyClass in it. I think I see what you are trying to say. If I understand, you don't like the fact that there is some dependency that the worker thread knows about the MyClass class. I think one way to fix this is to use what is sometimes called a Strategy Pattern or Dependency Injection. Rather than have Worker know about MyClass, you inject the information into Worker, thus breaking the dependency.
In plain English, I think Worker needs to know about the context in which it's running, so let's call this dependency a "context"
public class Worker { private Context localCntxt;
public Worker( Context c ) { localCntxt = c; } public void doStuff() { //... String rootDir = localCntxt.getRootDir(); //... } }
Now just make MyClass a type of Context object:
public class Context { // used to be type MyClass private String rootDir; public void setRootDir( String s ) { rootDir = s; } public String getRootDir() { return rootDir; } }
Feel free to stuff Context full of every sort of variable that your classes might ever need.
Now you can easily make lots of workers all running in the same context, or even different contexts.
public class Main { public void main( String [] args ) { Context a = new Context(); Context b = new Context(); Context c = new Context();
a.setRootDir( "/" ); b.setRootDir( "/Users/Mark/pub" ); c.setRootDir( "/argle/bargle/blet/foo/bar" );
Worker w1 = new Worker( a ); // Three workers all Worker w2 = new Worker( a ); // running in the same Worker w3 = new Worker( a ); // Context Worker w4 = new Worker( b ); // A Different Context b Worker w5 = new Worker( c ); // And Context c } }
I hope this made some sense to you.
Patricia Shanahan - 05 May 2007 21:46 GMT >> I feel it is kind of silly that in order to get to know what the root >> directory it is, Worker is forced to have a MyClass in it. [quoted text clipped - 5 lines] > than have Worker know about MyClass, you inject the information into > Worker, thus breaking the dependency. This sort of approach is certainly something to keep in mind, in case the situation gets sufficiently complicated to warrant it. I might do it that way from the start if this were a public interface that cannot be changed in the future.
However, if the root directory is the only thing that needs this sort of handling, and the code can be refactored later if it looks like a good idea, I would give this the KISS treatment.
To do what seems to be the end objective, static access to the args[0] from the main call:
public class MyClass { private static String rootDir;
public static String getRootDir() { // Optionally test for null and throw exception return rootDir; }
public static void main(String[] args) { rootDir = args[0]; } }
A worker can call MyClass.getRootDir() to get the root directory name. Short of nasty reflection abuse, a worker cannot modify rootDir.
Patricia
Mark Space - 06 May 2007 02:26 GMT > However, if the root directory is the only thing that needs this sort of > handling, and the code can be refactored later if it looks like a good > idea, I would give this the KISS treatment. I see what you are saying, and it's quite good. However, the OP himself raised the objection, so I thought I'd pop up with a possible solution. The program he's working on may in fact be rather complicated; we may have only seen a short example here on the news group. Or he may be trying to learn better techniques, so the added complexity would be useful for the example it provides. (Not that I think my example is all that great.) Besides, the dependency injection style is often much easier to break down for unit testing.
Knute Johnson - 06 May 2007 02:49 GMT >>> I feel it is kind of silly that in order to get to know what the root >>> directory it is, Worker is forced to have a MyClass in it. [quoted text clipped - 35 lines] > > Patricia Access to rootDir needs synchronization if it is going to be accessed by multiple threads which is guaranteed to be the case here.
 Signature Knute Johnson email s/nospam/knute/
Tom Hawtin - 06 May 2007 03:52 GMT >> public static void main(String[] args) { >> rootDir = args[0];
> Access to rootDir needs synchronization if it is going to be accessed by > multiple threads which is guaranteed to be the case here. Counterpoint of pedantry: Except for obscure things such as finalisers and assuming the initialisation of the class does not start any threads and the class is being started as a normal program, then there is a happens-before relation between any started threads and the single assignment.
OTOH, I would avoid statics on the principle of no broken windows. Or I think in the case of statics it can be termed the principle of "no crapping on the carpet".
Tom Hawtin
Knute Johnson - 06 May 2007 04:39 GMT >>> public static void main(String[] args) { >>> rootDir = args[0]; [quoted text clipped - 7 lines] > happens-before relation between any started threads and the single > assignment. Isn't that only true if assignment occurs in the constructor? We don't have a whole program here but I thought you could not ensure that assignment 'happens before' any other access to this variable given the possible instruction reordering. On the other hand after the constructor is complete it should be safe assuming it is not assigned again.
> OTOH, I would avoid statics on the principle of no broken windows. Or I > think in the case of statics it can be termed the principle of "no > crapping on the carpet". There is no doubt that many will shudder at the thought :-).
 Signature Knute Johnson email s/nospam/knute/
Tom Hawtin - 06 May 2007 09:28 GMT > Isn't that only true if assignment occurs in the constructor? We don't > have a whole program here but I thought you could not ensure that > assignment 'happens before' any other access to this variable given the > possible instruction reordering. On the other hand after the > constructor is complete it should be safe assuming it is not assigned > again. The point is that the assignment is more or less the first thing the program does. Any threads are created afterwards. It's the starting of threads that gives the happens-before, not the static initialisation.
(For (instance) constructors, the variable would need to be final and the instance should not leak before the end of the constructor.)
Tom Hawtin
Roman - 04 May 2007 20:13 GMT www pisze:
> Hi, > [quoted text clipped - 33 lines] > > Thank you for your help. you cannot assign value to final variable in an any method (even constructors). You have to decide: final or assigne value during declaration. Regards, Roman
visionset - 04 May 2007 20:35 GMT >> I thought final variable can be assigned value once and only once. And >> this can be done inside constructor. Cited from this web page: >> http://renaud.waldura.com/doc/java/final-keyword.shtml in Final Field >> part.
> you cannot assign value to final variable in an any method (even > constructors). You have to decide: final or assigne value during > declaration. That is not true, a final instance variable (attribute) can be assigned inline or in the constructor.
 Signature Mike W
Tom Hawtin - 04 May 2007 20:17 GMT > I want to have a static field variable. I want to initiate it once and > don't want it to be changed any more. So
> public final static rootDir; //error: saying rootDir may not be ^String
> initialized
> public static void main(String[] args) > { > String str = args[0]; > MyClass class = new MyClass(str); It doesn't really make sense for this kind of variable to be static. Any code that uses the variable, directly or indirectly, is specifying the interpretation of class loader scope. Stick with instance variables, and keep dependencies as narrow as possible.
Use static fields judiciously. They are good for constants[1], really difficult for caches and hopeless for practically anything else.
Tom Hawtin
[1] Constants as in things that cannot change, not just as in the JLS "compile-time constant".
Arne Vajhøj - 05 May 2007 16:54 GMT > I want to have a static field variable. I want to initiate it once and > don't want it to be changed any more. So [quoted text clipped - 28 lines] > this can be done inside constructor. Cited from this web page: > http://renaud.waldura.com/doc/java/final-keyword.shtml in Final Field part. As other have already stated then the whole construct is not good.
But to complete the understanding of final and initialization, then the following compiles:
public class FinalDemo { public final static String s1; public final String s2; static { s1 = "A"; } public FinalDemo() { s2 = "B"; } }
final static can be initialized in "static constructor" and final non-static can be initialized in constructor.
Which I think makes sense.
Arne
Knute Johnson - 06 May 2007 02:51 GMT >> I want to have a static field variable. I want to initiate it once and >> don't want it to be changed any more. So [quoted text clipped - 53 lines] > > Arne Arne:
Is there any practical difference between;
static final String s1 = "A";
and
static final String s1; static { s1 = "A" };
Thanks,
 Signature Knute Johnson email s/nospam/knute/
Stefan Ram - 06 May 2007 02:58 GMT >Is there any practical difference between; >static final String s1 = "A"; >and >static final String s1; >static { s1 = "A" }; If you deem it a »practical difference« that the second text will not compile.
Arne Vajhøj - 06 May 2007 03:04 GMT >> Is there any practical difference between; >> static final String s1 = "A"; [quoted text clipped - 4 lines] > If you deem it a »practical difference« > that the second text will not compile. It compiles with the missing semicolon added.
Arne
Knute Johnson - 06 May 2007 03:26 GMT >>> Is there any practical difference between; >>> static final String s1 = "A"; [quoted text clipped - 8 lines] > > Arne Picky picky a semi-colon here or there :-).
 Signature Knute Johnson email s/nospam/knute/
Arne Vajhøj - 06 May 2007 03:06 GMT >> But to complete the understanding of final and initialization, then >> the following compiles: [quoted text clipped - 15 lines] >> >> Which I think makes sense.
> Is there any practical difference between; > [quoted text clipped - 4 lines] > static final String s1; > static { s1 = "A" }; I do not think so.
The reason to use the "static constructor" (if you will forgive me for using the C# term) is if you need multiple statements to calculate the value.
Arne
Arne
Tom Hawtin - 06 May 2007 03:58 GMT >> Is there any practical difference between; >> [quoted text clipped - 6 lines] > > I do not think so. Yup, javap -c should show identical byte code.
> The reason to use the "static constructor" (if you will forgive > me for using the C# term) Nooooo!!1! "static initialiser" (although technically initialiser should be missplled)
> is if you need multiple statements to > calculate the value. Some people prefer defining a function to do that. I prefer the static initialiser. Note, you can't qualify with the class in the assignment ("FinalDemo.s1 = "A";"). However, never use that as an excuse to miss the final!
Tom Hawtin
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|