Java Forum / General / August 2007
final parameters
gaijinco - 16 Aug 2007 20:12 GMT I used C++ for quite a while and for a year or so I´ve been using Java.
One of the most important things that rarely was used in C++ was to make sure that, if applicable, all the parameters were declared const. Even if rarely used, almost every book out there says it's good practice.
Now for Java I have never ever read or heard someone saying that, if applicable, the parameters should be declared final.
Why is that? Is there a backlash in Java doing so? Or you should really make all appropiate parameters final?
Thanks.
gaijinco - 16 Aug 2007 20:21 GMT I of course mean parameters in a function, like for example:
public class Foo { // Atributes private int x;
// Constructor public Foo(final int x) { this.x = x; }
// Setters private void setX(final int x) { this.x = x; } }
Daniel Pitts - 16 Aug 2007 20:50 GMT > I used C++ for quite a while and for a year or so I´ve been using > Java. [quoted text clipped - 11 lines] > > Thanks. The problem is that the meaning of "const" in C is very different from the meaning of "final" in Java.
const implies (and somewhat enforces) that the value of the passed argument will not be modified by the method...
final on the other hand only says that the reference to that argument won't be changed.
public class FinalTest { public String value;
public static void main(String...args) { FinalTest ft = new FinalTest() ft.value = "Hello"; System.out.println("before: "+ft.value); finalParam(ft); System.out.println("after: "+ft.value); } public static void finalParam(final FinalTest test) { test.value = "World!"; /* test = new FinalTest(); // compile Error */ } }
This difference is primarily from the fact that java passes everything by primative value or object reference, never by object value, and that java has no way to specify that an object reference should be considered immutable (which is what const really means in c++)
Alexey - 16 Aug 2007 21:18 GMT > > I used C++ for quite a while and for a year or so I´ve been using > > Java. [quoted text clipped - 42 lines] > that java has no way to specify that an object reference should be > considered immutable (which is what const really means in c++) That's right, but I believe the OP was talking about the performance benefits one might draw in C++ from "const" parameter declarations. I actually recall reading something to that effect (declaring everything that can be "final") with regard to Java back in the 1.0 days. And to this day I find old code of mine littered with "final" parameters. If I'm not mistaken the idea behind this is to help the compiler along with various optimizations (stack vs heap memory?). As you've rightly pointed out, many such optimizations are simply impossible in Java because all non-primitive objects are passed by "pointer" only. So in fact, final only applies to the value of the pointer, not the object it's pointing to and it's very tough for the compiler to ascertain that a particular object exhibits immutable behavior.
Interestingly, there are situations where the language _requires_ use of finals and that's with anonymous inner classes accessing values from their invocation context:
public void test(final int val1, int val2) { final int val3 = 0; int val4 = 0; EventQueue.invokeLater(new Runnable() { public void run() { System.out.println(val1); // legal System.out.println(val2); // won't compile System.out.println(val3); // legal System.out.println(val4); // won't compile } }); }
Roedy Green - 16 Aug 2007 22:31 GMT >Why is that? Is there a backlash in Java doing so? Or you should >really make all appropiate parameters final? The problem is making parms final exposes a detail to the outside world that is none of their bloody business. It is a matter purely of implementation within the method. I find that offensive though I am usually a huge fan of final.
It is not as though Java CAN EVER change the values of the caller's variables (other than changing their fields).
 Signature Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
Stefan Ram - 16 Aug 2007 22:46 GMT >The problem is making parms final exposes a detail to the outside >world that is none of their (...) business. The finalness is not part of the interface.
interface I { void f( int x ); }
class C implements I { public void f( final int x ){ return 0; }}
The above should compile, and you are free to remove or introduce the »final« anytime without breaking any interfaces or clients.
Stefan Ram - 16 Aug 2007 22:49 GMT Supersedes: <final-20070816234537@ram.dialup.fu-berlin.de>
>The problem is making parms final exposes a detail to the outside >world that is none of their (...) business. The finalness is not part of the interface.
interface I { void f( int x ); }
class C implements I { public void f( final int x ){} }
The above should compile, and you are free to remove or introduce the »final« anytime without breaking any interfaces or clients.
Supersedes: <final-20070816234537@ram.dialup.fu-berlin.de>
gaijinco - 17 Aug 2007 04:13 GMT But in the end if I have a method which receive a parameter that doesn't have to change it would still be a good idea to make it final, doesn't it?
For example in a constructor, obviously you will never want that a parameter in a constructor to lose its value, at least before making the correponding assigment with the appropiate atribute.
So even if it doesn't accomplish the same sort of optimization, it just make sense, doesn't it?
class Foo() { int x;
public Foo(final int x) { // }
}
class Foo() { int x;
public Foo(int x) { // }
}
Ben Phillips - 17 Aug 2007 04:35 GMT > For example in a constructor, obviously you will never want that a > parameter in a constructor to lose its value, at least before making > the correponding assigment with the appropiate atribute. The parameter cannot "lose its value".
A parameter in a constructor (or method, static or otherwise) is really a local variable in disguise, one that gets preassigned with the result of evaluating some expression where the method or constructor got called.
What happens if I execute
Foobar f = new Foobar(someList)
private List myList; public Foobar (List theList) { myList = theList; }
is more or less as follows.
A reference "f" is allocated on the stack. The expression "someList" is evaluated, presumably to a List reference (under the hood this is most probably a machine pointer). Memory for a new Foobar is allocated (here is where any OOME is thrown), including for a reference "myList". A reference "theList" is allocated on the stack. The pointer to the list referenced by "someList" is copied to "theList". The code being run jumps into the Foobar constructor's code. The pointer at "theList" is copied to "myList". At some time during all of this, the "f" reference is made to point to the new Foobar object being constructed.
This is what happens conceptually; there may well be optimizations in practise, especially with JIT compiling.
In any event, if "someList" is reassigned before the constructor is called, the newer value is what gets copied to "theList"; otherwise the older value is copied. (With concurrency, a race condition may make it a toss-up which version gets copied.)
But "theList" gets one version or the other, and this version is copied to "myList". If "someList" is changed to point to another List (or to "null") after it was copied to "theList", "theList" still points to the earlier List, and "myList" still ends up pointing to that List.
Maybe it's easier if an integer is used. A machine pointer and an integer are probably the same thing at the bottom level, modulo the hardware you use, anyway, but some things should be clearer.
So "someInteger" is equal to, say, 5. When the constructor is called, "theInteger" gets set equal to 5. If "someInteger" is changed to 6 by some concurrent code, "theInteger" remains equal to 5, because it's a different integer; it's a copy. Eventually "myInteger" ends up being 5 (not 6).
The tricky case is when the object is mutable. So "someList" points to a list, "theList" gets pointed to the same list, and someone adds something to the list, then "myList" gets pointed to the list. Since there's only the one list, the change is visible everywhere, but it wouldn't have mattered. If the change happened before "theList" was assigned, "myList" still ends up pointing to this list, and still it has the extra item. If "myList" points to the list before the change is made to the list, "myList" stays pointing to the same list, but someone iterating over the list a couple of times with "for (Foo x : myList)" may see a new item the second time they do so. This isn't peculiar to constructors, and it wouldn't be prevented by making "theList" final, or even by making "myList" final. This doesn't stop the list being modified; it only stops the named reference being pointed at some other list later, or becoming null.
Even using "Collections.unmodifiableList" somewhere along the way won't stop someone changing the list that has a reference to the original list object and not just the wrapper returned by the aforementioned method, or make the list change invisible to "myList" users.
To make the list never get modified of course it can be wrapped in "unmodifiableList" as soon as it's built and populated appropriately, and all direct references to the original, mutable list discarded.
To make the Foobar constructor not see any modifications means passing it a copy of the list: "Foobar f = new Foobar(new ArrayList(someList));"
To make the Foobar object unable to ever see any modifications the constructor itself can copy the list: "myList = new ArrayList(theList);"
Even then the Foobar might have methods "return myList;" and then see changes made to the list; "return Collections.unmodifiableList(myList);" prevents that, and a single unmodifiable view can be constructed and stored in an ivar to return instead of making a new one each time of course. Either way the Foobar has a private copy of the list and doesn't let anyone else ever get hold of a reference to it that can be used to change it.
And even then, if the list contains objects that are themselves modifiable, such as more lists, or sets or maps or whatever, one of these might be altered and the change visible through the Foobar instance's "myList" ivar.
Needless to say, it is often best to use unmodifiable object types, including Collections.unmodifiableFoo, liberally, and encapsulate all mutable state. Making ivars "final" helps make and enforce unmodifiable object types.
Making constructor (or other) parameters "final" doesn't do anything of the sort.
Roedy Green - 17 Aug 2007 21:25 GMT On Thu, 16 Aug 2007 23:35:52 -0400, Ben Phillips <b.phillips@a5723mailhost.net> wrote, quoted or indirectly quoted someone who said :
>The expression "someList" is evaluated, presumably to a List reference >(under the hood this is most probably a machine pointer). The Java Byte Code interpreter model pushes evaluated parameters to the stack when you make a call. When the callee wakes up the parameters are sitting there on the stack looking for all the world like local variable slots on the stack. That way the callee can treat parameters and locals almost identically.
Primitive values are pushed directly to the stack. References are pushed as a 32- bit (or 64-bit) pointer.
Read the Goldfish bowl book. See http://mindprod.com/jgloss/jvm.html The JVM spec gives immense latitude how it is ACTUALLY implemented so long as it behaves logically like the model.
 Signature Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
Eric Sosman - 17 Aug 2007 05:06 GMT > But in the end if I have a method which receive a parameter that > doesn't have to change it would still be a good idea to make it final, > doesn't it? It doesn't make much difference. Personally, I declare parameters as final if
1) The consequences of changing them would be bad (which is seldom a concern in a pass-by-value language), or
2) They will be used by inner classes created within the method (in which case Java *requires* them to be final).
Absent one or the other of these, I don't use final parameters: they're just visual clutter, something that Java doesn't lack.
> For example in a constructor, obviously you will never want that a > parameter in a constructor to lose its value, at least before making > the correponding assigment with the appropiate atribute. If the constructor is doing so many things that it could get confused, the constructor is probably doing too much. Again, opinions vary. Most of my constructors consist of a little bit of validation and then a few assignments to instance variables, not enough code to offer a reading comprehension challenge.
> So even if it doesn't accomplish the same sort of optimization, it > just make sense, doesn't it? I don't feel that way. A parameter without `final' is like a fish without a bicycle.
 Signature Eric Sosman esosman@ieee-dot-org.invalid
Chris Dollin - 17 Aug 2007 09:03 GMT > But in the end if I have a method which receive a parameter that > doesn't have to change it would still be a good idea to make it final, > doesn't it? Why?
> For example in a constructor, obviously you will never want that a > parameter in a constructor to lose its value, at least before making > the correponding assigment with the appropiate atribute. If the constructor is so big that you can't /see/ that the parameter is erroneously assigned, it's too big anyway. (And won't your unit tests fail if you mess up the assignments?)
 Signature Chris "assignment considered ... dangerous." Dollin
Hewlett-Packard Limited Cain Road, Bracknell, registered no: registered office: Berks RG12 1HN 690597 England
Oliver Wong - 17 Aug 2007 23:57 GMT > But in the end if I have a method which receive a parameter that > doesn't have to change it would still be a good idea to make it final, > doesn't it? [...]
> So even if it doesn't accomplish the same sort of optimization, it > just make sense, doesn't it? As most poster have told you, it doesn't really make much pragmatic difference either way.
However, it seems most of these posters therefore conclude "don't bother making your params final". I'm of the opposite opinion: Since it doesn't matter either way, make them final by default. I like having as many of my variables final as possible.
- Oliver
Roedy Green - 17 Aug 2007 21:27 GMT > The finalness is not part of the interface. That is reassuring. I did not in the least like the idea I would force clients to recompile as I tweaked the finalness of parameters.
I don't think the finalness should be exposed in generated Javadoc either. I have not experimented to see if it is.
 Signature Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
Thomas Hawtin - 17 Aug 2007 21:42 GMT > I don't think the finalness should be exposed in generated Javadoc > either. I have not experimented to see if it is. On parameters, no it isn't. At least not in any sensible version. Really early implementations did, but you shouldn't see them anymore. final is displayed when applied to fields, methods and classes.
Tom hawtin
Roedy Green - 17 Aug 2007 22:28 GMT >On parameters, no it isn't. At least not in any sensible version. Really >early implementations did, but you shouldn't see them anymore. final is >displayed when applied to fields, methods and classes. Good to hear. That is how it should be.
 Signature Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
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 ...
|
|
|