effects of "final" in parameters

D

Daniel Dyer

There's nothing wrong with that approach, but I don't think it is
necessary. It assumes that there are situations in which you would want
to re-assign method parameters. But since Java is pass-by-value, there
is no useful reason to reassign method parameters. I leave them
non-final and assume that they never change (yes, I know about
assumption being the mother of all ****-ups).

I should clarify that I was thinking more about reference types here
rather than primitives. I can think of no good reason to assign a new
value to a (passed-by-value) reference parameter. I can imagine there
would be situations in which it might make sense to assign a new value to
a primitive parameter to avoid duplication, but I can't recall having done
it myself any time recently.

Dan.
 
R

Roedy Green

There's nothing wrong with that approach, but I don't think it is
necessary. It assumes that there are situations in which you would want
to re-assign method parameters. But since Java is pass-by-value, there is
no useful reason to reassign method parameters. I leave them non-final
and assume that they never change (yes, I know about assumption being the
mother of all ****-ups).


The thing I don't like about declaring parameters final is the
finality of them is an implementation detail. It has no business being
exposed in the method signature. It is absolutely none of the
caller's business.
 
R

Roedy Green

It
seems a silly restriction to me, one that has little
purpose except to introduce unneeded local variables to
hold copies of the arguments.

When you do that later on in the code you will very likely use the
original rather than the modified value in the secondary local,
especially if you add some code in later. That is a needless danger.
It also needlessly increases stack depth, not good for anything to do
with recursion.
 
R

Roedy Green

Sorry guys, I made mistake about Checkstyle rules, they are not as
restrictive as I wrote. There are rules which advice that NON-MODIFIED
variables / parameters (there were no assignment in code after
declaration) COULD be declared as final - but not that "ALL variables /
parameters should be final".

that is quite different. That is a nice reassurance you don't have to
look for any mods. If you need to add them, remove the final.
 
D

Dimitri Maziuk

Daniel Dyer sez:
I should clarify that I was thinking more about reference types here
rather than primitives. I can think of no good reason to assign a new
value to a (passed-by-value) reference parameter. I can imagine there
would be situations in which it might make sense to assign a new value to
a primitive parameter to avoid duplication ...

void foo( String host ) {
if( host.indexOf( '.' ) < 0 )
host = host + getMyDomainName();
connect( host );
...
}

of course, you could replace that with far more readable
connect( (host.indexOf( '.' ) < 0 ? host + getMyDomainName() : host) );

Dima
 
E

Eric Sosman

Daniel Dyer wrote On 03/01/06 14:42,:
I should clarify that I was thinking more about reference types here
rather than primitives. I can think of no good reason to assign a new
value to a (passed-by-value) reference parameter. I can imagine there
would be situations in which it might make sense to assign a new value to
a primitive parameter to avoid duplication, but I can't recall having done
it myself any time recently.

Here's a real-life example from a class that does
rational arithmetic using BigInteger to represent the
numerator and denominator. For correctness and security
(and even for speed), it's important that the fraction
be maintained in a "canonical" form: numerator and
denominator immutable and relatively prime, denominator
non-negative, denominator zero only for infinities and
NaNs. Here's the actual code for one of the constructors,
warts and all:

public Fraction(BigInteger num, BigInteger den) {
/* Defend against mutable subclasses of BigInteger (see Bloch
* "Effective Java," Item 13).
*/
if (num.getClass() != BigInteger.class)
num = new BigInteger(num.toByteArray());
if (den.getClass() != BigInteger.class)
den = new BigInteger(den.toByteArray());

int s = den.signum();
if (s <= 0) {
if (s == 0) {
this.num = BigInteger.valueOf(num.signum());
this.den = BigInteger.ZERO;
return;
}
num = num.negate();
den = den.negate();
}

BigInteger gcd = num.gcd(den);
if (! gcd.equals(BigInteger.ONE)) {
num = num.divide(gcd);
den = den.divide(gcd);
}
this.num = num;
this.den = den;
}

It seems to me that the six reassignments to the
parameters make for clearer and crisper code than any
arrangement that would avoid them. The whole thing
operates as a sort of progressive refinement: We've got
three rules (actual BigIntegers, denominator non-negative,
fraction in lowest terms), and we handle them in three
short stages. If the supplied parameters fail to meet
a stage's requirement, they get replaced by corrected
parameters. If there were ten requirements instead of
three, we could insert seven more test-and-correct
stages in the same pattern. (In fact, this has already
happened: The original version had only two requirements
and two test-and-correct stages, because the notion of
mutable BigIntegers hadn't occurred to me. After I read
Bloch I went back and inserted the necessary check, with
no disruption at all to the structure of the code.)

Of course, this sort of begs the question: I think
it's perfectly all right to assign new values to method
parameters, so I exhibit some of my own code as proof.
Not exactly the most rigorous argument, is it? So here's
the interesting part: Re-write the constructor but with
the new signature

public Fraction(final BigInteger num,
final BigInteger den)

and explain why the rewrite is better than the original.
(And if you convince me, permit me to swipe your code.)
 
D

Daniel Dyer

Daniel Dyer wrote On 03/01/06 14:42,:

Here's a real-life example from a class that does
rational arithmetic using BigInteger to represent the
numerator and denominator. For correctness and security
(and even for speed), it's important that the fraction
be maintained in a "canonical" form: numerator and
denominator immutable and relatively prime, denominator
non-negative, denominator zero only for infinities and
NaNs. Here's the actual code for one of the constructors,
warts and all:

OK, you've convinced me. I wasn't saying the reassignment was inherently
bad, just that I never seem to need to do it. I guess that's because I've
got into the habit of introducing new variables or rearranging things some
other way without even thinking about it so that I avoid the need. It's
probably a habit I picked up in Pascal where you really can
pass-by-reference and as such the re-assignment has implications elsewhere
and so should be used with care. I also didn't consider immutable objects
like String and BigInteger, which fall into the same category as
primitives in this situation.
So here's
the interesting part: Re-write the constructor but with
the new signature

public Fraction(final BigInteger num,
final BigInteger den)

and explain why the rewrite is better than the original.
(And if you convince me, permit me to swipe your code.)

I wouldn't dare suggest that I could improve on your code.

Dan.
 
C

Chris Smith

Dimitri Maziuk said:
Not really, just a logical evolution of Java philosophy. We begin
with programmers incapable of freeing heap space they used and
naturally progress to programmers incapable of remembering they've
modified method parameter two lines ago.

You say this as if freeing heap space is always trivial. It turns out
that programmers who work in manually managed dynamic memory
environments adopt a large number of habits, mostly involving not
sharing immutable objects, which complicate application code just
because of the problem of tracking object ownership. Much of the time,
this forces multiple copies of the same logically identical object, and
introduces some potential bugs into design.

There is a reason why virtually all object oriented programming
languages specify a garbage collected heap.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
C

Chris Smith

Chris Uppal said:
Some people seem to use it a lot. I consider it pointless myself. (Unless, of
course, I /have/ to use it to satisfy the compiler that an inner class won't
break if it refers to the parameter). If I don't want to change the value of a
parameter then I don't change it -- adding decoration to tell the compiler that
I don't intend to change it, so that it can then check that I don't change it,
seems a singularly unproductive exercise.

Absolutely. This is a case where the largest possible effect of the
word "final" is within the implementation of one method of one class.
If a method is large enough that you can't remember whether you meant to
change some parameter, then you've got bigger problems than whether to
declare the parameter final or not.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
C

Chris Smith

robson said:
- this should answer your question Eric - yes, according to Java gurus
it is better to introduce local variable and assing value to it.

I don't think that was Eric's question, so much as his opinion that it's
not better. I agree with Eric. As long as the meaning of the parameter
is not changed, reassigning it with a simpler form of the value is
entirely legitimate and perfectly clear.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
S

Stefan Ram

Chris Smith said:
You say this as if freeing heap space is always trivial. It turns out
that programmers who work in manually managed dynamic memory
environments adopt a large number of habits, mostly involving not
sharing immutable objects, which complicate application code just
because of the problem of tracking object ownership.

|Your essay made me remember an interesting phenomenon I saw in
|one system I worked on. There were two versions of it, one in
|Lisp and one in C++. The display subsystem of the Lisp version
|was faster. There were various reasons, but an important one
|was GC: the C++ code copied a lot of buffers because they got
|passed around in fairly complex ways, so it could be quite
|difficult to know when one could be deallocated. To avoid that
|problem, the C++ programmers just copied. The Lisp was GCed,
|so the Lisp programmers never had to worry about it; they just
|passed the buffers around, which reduced both memory use and
|CPU cycles spent copying.
 
D

Dimitri Maziuk

Chris Smith sez:
You say this as if freeing heap space is always trivial.

No, not always. Only most of the time.
There is a reason why virtually all object oriented programming
languages specify a garbage collected heap.

Uh-huh. JRT specs, for example, with their request for non-gc'ed
heap areas.

Dima
 
D

Dimitri Maziuk

Stefan Ram sez:
|Your essay made me remember an interesting phenomenon I saw in
|one system I worked on. There were two versions of it, one in
|Lisp and one in C++. The display subsystem of the Lisp version
|was faster. There were various reasons, but an important one
|was GC: the C++ code copied a lot of buffers because they got
|passed around in fairly complex ways, so it could be quite
|difficult to know when one could be deallocated. To avoid that
|problem, the C++ programmers just copied. The Lisp was GCed,
|so the Lisp programmers never had to worry about it; they just
|passed the buffers around, which reduced both memory use and
|CPU cycles spent copying.

That reminds me of David Bowie being a crappy singer:
"have you been to his concerts?"
"no, my friend Joe sang me some of his stuff over the phone."

So the programmers didn't know when to deallocate the original
buffer and decided to solve that problem by making more copies
of it. Sounds like the problem was, they should've hired
programmers with a clue.

They may have had a reason for not creating a factory-type
object to manage buffer allocations, but it's kinda hard to
tell from a quote posted by somebody named Stephan attributed
to somebody named mikel.

Dima
 
R

Raymond DeCampo

Eric said:
Daniel Dyer wrote On 03/01/06 14:42,:



Here's a real-life example from a class that does
rational arithmetic using BigInteger to represent the
numerator and denominator. For correctness and security
(and even for speed), it's important that the fraction
be maintained in a "canonical" form: numerator and
denominator immutable and relatively prime, denominator
non-negative, denominator zero only for infinities and
NaNs. Here's the actual code for one of the constructors,
warts and all:

public Fraction(BigInteger num, BigInteger den) {
/* Defend against mutable subclasses of BigInteger (see Bloch
* "Effective Java," Item 13).
*/
if (num.getClass() != BigInteger.class)
num = new BigInteger(num.toByteArray());
if (den.getClass() != BigInteger.class)
den = new BigInteger(den.toByteArray());

int s = den.signum();
if (s <= 0) {
if (s == 0) {
this.num = BigInteger.valueOf(num.signum());
this.den = BigInteger.ZERO;
return;
}
num = num.negate();
den = den.negate();
}

BigInteger gcd = num.gcd(den);
if (! gcd.equals(BigInteger.ONE)) {
num = num.divide(gcd);
den = den.divide(gcd);
}
this.num = num;
this.den = den;
}

It seems to me that the six reassignments to the
parameters make for clearer and crisper code than any
arrangement that would avoid them. The whole thing
operates as a sort of progressive refinement: We've got
three rules (actual BigIntegers, denominator non-negative,
fraction in lowest terms), and we handle them in three
short stages. If the supplied parameters fail to meet
a stage's requirement, they get replaced by corrected
parameters. If there were ten requirements instead of
three, we could insert seven more test-and-correct
stages in the same pattern. (In fact, this has already
happened: The original version had only two requirements
and two test-and-correct stages, because the notion of
mutable BigIntegers hadn't occurred to me. After I read
Bloch I went back and inserted the necessary check, with
no disruption at all to the structure of the code.)

Of course, this sort of begs the question: I think
it's perfectly all right to assign new values to method
parameters, so I exhibit some of my own code as proof.
Not exactly the most rigorous argument, is it? So here's
the interesting part: Re-write the constructor but with
the new signature

public Fraction(final BigInteger num,
final BigInteger den)

and explain why the rewrite is better than the original.
(And if you convince me, permit me to swipe your code.)

Sounds like a challenge, I can't resist. (Although, philosophically, I
do not have a problem with the original code.)

// Note I renamed your parameters so I wouldn't have
// "this." everywhere
public Fraction(final BigInteger numerator, final BigInteger
denominator)
{
if (numerator.getClass() == BigInteger.class)
{
num = numerator;
}
else
{
// Defend against mutable subclasses of BigInteger
// (see Bloch "Effective Java," Item 13).
num = new BigInteger(numerator.toByteArray());
}

if (denominator.getClass() == BigInteger.class)
{
den = denominator;
}
else
{
// Defend against mutable subclasses of BigInteger
// (see Bloch "Effective Java," Item 13).
den = new BigInteger(denominator.toByteArray());
}
// At this point I'm done with the parameters

int s = den.signum();
if (s <= 0)
{
if (s == 0)
{
num = BigInteger.valueOf(num.signum());
den = BigInteger.ZERO;
return;
}
num = num.negate();
den = den.negate();
}

BigInteger gcd = num.gcd(den);
if (! gcd.equals(BigInteger.ONE))
{
num = num.divide(gcd);
den = den.divide(gcd);
}
}

You can decide for yourself if this is better. Oh, and I "fixed" your
brace style too. :)

HTH,
Ray
 
E

Ed

Forgive the shameful OTness, but I was in a team of Bloch-pilgrims once
and we were defensive-coding maniacs. Then someone pointed out that,
although, "Effective Java," is a fabulous book, if it has one flaw it's
its failure to note that defensive coding should only be used when
you're, "Not sure who your client is." IOW, if your code is sitting
behind a public interface, then defensive-code away; but if the client
is being written by another team sitting three cubicles away from you,
then defensive coding can be overkill.

..ed
 
D

Daniel Dyer

Nooooooooooooo... that way lies dynamic typing.

Commercial code, apparently, averages around one defect per 20 lines.
You really want to pick up on and remove those problems.

Software can be quite complex. It's kind of handy to write down exactly
what is expected in the programming language itself.

Tom Hawtin

Tom, you appear to be posting from the future. Either your PC clock is
screwed or you're posting from an oil rig in the North Sea with its own
timezone.

Dan.
 
T

Thomas Hawtin

Ed said:
Forgive the shameful OTness, but I was in a team of Bloch-pilgrims once
and we were defensive-coding maniacs. Then someone pointed out that,
although, "Effective Java," is a fabulous book, if it has one flaw it's
its failure to note that defensive coding should only be used when
you're, "Not sure who your client is." IOW, if your code is sitting
behind a public interface, then defensive-code away; but if the client
is being written by another team sitting three cubicles away from you,
then defensive coding can be overkill.

Nooooooooooooo... that way lies dynamic typing.

Commercial code, apparently, averages around one defect per 20 lines.
You really want to pick up on and remove those problems.

Software can be quite complex. It's kind of handy to write down exactly
what is expected in the programming language itself.

Tom Hawtin
 
R

Raymond DeCampo

Ed said:
Raymond DeCampo wrote:




Forgive the shameful OTness, but I was in a team of Bloch-pilgrims once
and we were defensive-coding maniacs. Then someone pointed out that,
although, "Effective Java," is a fabulous book, if it has one flaw it's
its failure to note that defensive coding should only be used when
you're, "Not sure who your client is." IOW, if your code is sitting
behind a public interface, then defensive-code away; but if the client
is being written by another team sitting three cubicles away from you,
then defensive coding can be overkill.

Please be more careful with your attributions. I was not responsible
for the above code or the comment.

Ray
 
C

Chris Smith

Raymond DeCampo said:
You can decide for yourself if this is better. Oh, and I "fixed" your
brace style too. :)

Well, for one thing, it prevents declaring the fields with a "final"
modifier. That's pretty bad. Much better to have final fields but non-
final parameters than the other way around (assuming this is an
immutable class, which seems likely).

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
R

Raymond DeCampo

Chris said:
Well, for one thing, it prevents declaring the fields with a "final"
modifier. That's pretty bad. Much better to have final fields but non-
final parameters than the other way around (assuming this is an
immutable class, which seems likely).

I agree with you there. But that was not part of the requirements. ;-)

Ray
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,434
Messages
2,571,691
Members
48,796
Latest member
Greg L.

Latest Threads

Top