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.)