这个观点来自《重构-----改善既有代码的设计》
The code assigns to a parameter.
02Use a temporary variable instead.
03int discount (int inputVal, int quantity, int yearToDate) {
if (inputVal > 50) inputVal -= 2;
...
int discount (int inputVal, int quantity, int yearToDate) {
int result = inputVal;
if (inputVal > 50) result -= 2;
...
First let me make sure we are clear on the phrase “assigns to a parameter.” This means that if you pass in an object named foo, in the parameter, assigning to the parameter means to change foo to refer to a different object. I have no problems with doing something to the object that was passed in; I do that all the time. I just object to changing foo to refer to another object entirely:
06void aMethod(Object foo) {
foo.modifyInSomeWay(); // that's OK
foo = anotherObject; // trouble and despair will follow you
...
07
The reason I don’t like this comes down to lack of clarity and to confusion between pass by value and pass by reference. Java uses pass by value exclusively (see later), and this discussion is based on that usage.
08With pass by value, any change to the parameter is not reflected in the calling routine. Those who have used pass by reference will probably find this confusing.
09The other area of confusion is within the body of the code itself. It is much clearer if you use only the parameter to represent what has been passed in, because that is a consistent usage.
10In Java, don’t assign to parameters, and if you see code that does, apply Remove Assignments to Parameters.
11Of course this rule does not necessarily apply to other languages that use output parameters, although even with these languages I prefer to use output parameters as little as possible.
Create a temporary variable for the parameter.
Replace all references to the parameter, made after the assignment, to the temporary variable.
Change the assignment to assign to the temporary variable.
Compile and test.
If the semantics are call by reference, look in the calling method to see whether the parameter is used again afterward. Also see how many call by reference parameters are assigned to and used afterward in this method. Try to pass a single value back as the return value. If there is more than one, see whether you can turn the data clump into an object, or create separate methods.
I start with the following simple routine:
14int discount (int inputVal, int quantity, int yearToDate) {
if (inputVal > 50) inputVal -= 2;
if (quantity > 100) inputVal -= 1;
if (yearToDate > 10000) inputVal -= 4;
return inputVal;
}
15
Replacing with a temp leads to
16int discount (int inputVal, int quantity, int yearToDate) {
int result = inputVal;
if (inputVal > 50) result -= 2;
if (quantity > 100) result -= 1;
if (yearToDate > 10000) result -= 4;
return result;
}
17
You can enforce this convention with the final
keyword:
int discount (final int inputVal, final int quantity, final int yearToDate) {
int result = inputVal;
if (inputVal > 50) result -= 2;
if (quantity > 100) result -= 1;
if (yearToDate > 10000) result -= 4;
return result;
}
19
I admit that I don’t use final
much, because I don’t find it helps much with clarity for short methods. I use it with a long method to help me see whether anything is changing the parameter.
Use of pass by value often is a source of confusion in Java. Java strictly uses pass by value in all places, thus the following program:
21class Param {
public static void main(String[] args) {
int x = 5;
triple(x);
System.out.println ("x after triple: " + x);
}
private static void triple(int arg) {
arg = arg * 3;
System.out.println ("arg in triple: " + arg);
}
}
22
produces the following output:
23arg in triple: 15
x after triple: 5
24
The confusion exists with objects. Say I use a date, then this program:
25class Param {
public static void main(String[] args) {
Date d1 = new Date ("1 Apr 98");
nextDateUpdate(d1);
System.out.println ("d1 after nextDay: " + d1);
Date d2 = new Date ("1 Apr 98");
nextDateReplace(d2);
System.out.println ("d2 after nextDay: " + d2);
}
private static void nextDateUpdate (Date arg) {
arg.setDate(arg.getDate() + 1);
System.out.println ("arg in nextDay: " + arg);
}
private static void nextDateReplace (Date arg) {
arg = new Date (arg.getYear(), arg.getMonth(), arg.getDate() + 1);
System.out.println ("arg in nextDay: " + arg);
}
}
26
It produces this output:
27arg in nextDay: Thu Apr 02 00:00:00 EST 1998
d1 after nextDay: Thu Apr 02 00:00:00 EST 1998
arg in nextDay: Thu Apr 02 00:00:00 EST 1998
d2 after nextDay: Wed Apr 01 00:00:00 EST 1998
28
Essentially the object reference is passed by value. This allows me to modify the object but does not take into account the reassigning of the parameter.
29 Java 1.1 and later versions allow you to mark a parameter as final;
this prevents assignment to the variable. It still allows you to modify the object the variable refers to. I always treat my parameters as final, but I confess I rarely mark them so in the parameter list.