deposit
:
void deposit(double amount) { this.balance = this.balance + amount; }The following message send:
account.deposit(35.0);could be rewritten as:
account.balance = account.balance + 35.0;The rule for rewriting is to first replace the message send with the body of the method definition, replacing the parameter names (
amount
, in this case) with the corresponding values of
the arguments (35.0, in this case). Also, the name this
is everywhere replaced by the name of the object that is receiving the
message (account
, in this case).
int x = 10; int y = x * x; String myName = "Bill";However, in a segment of code containing message sends, there is more going on than meets the eye.
double amount = 15.25; // line 1 account.deposit(amount); // line 2 double euros = account.convertBalance(1.10); // line 3The first line executes by binding the name
amount
to the
value 15.25. The second line calls a method that returns no value.
The next statement that executes is actually inside of the method
deposit
. When this statement completes, control passes
back to this fragment and the third line is executed. This line binds
the name euros
to the value returned by the
convertBalance
method. To calculate that value, control
must pass to the body of that method.
Thinking about method calls in terms of change of control flow is
closer to the way method calls actually take place. More formally,
when a method is called, the arguments to the method are first
evaluated. Next, the names of the method's parameters are bound to
the values of its arguments. Finally, control is passed to the first
line of the method, and execution continues inside of the body of the
method until either a return
is encountered, or there are no
more statements to execute. At this point, control passes back to the
call site -- the point where the method was originally called.
public class BankAccount { private int number; private double balance; private String name; public void deposit(double amount) { this.balance = this.balance + amount; } public void withdraw(double amount) { this.balance = this.balance - amount; } public void transferFrom(BankAccount other, double amount) { other.withdraw(amount); this.deposit(amount); } // And some other methods... }And now, let's draw pictures for what happens when the following fragment executes:
BankAccount account1 = new BankAccount(1234, 225.34, "Bill"); BankAccount account2 = new BankAccount(23455, 125.0, "George"); account1.transferFrom(account2, 75.0);The basic idea is to imagine that the machine has a bunch of pieces of scratch paper. When it needs to execute a sequence of statements such as the above, it gets a new sheet of paper, and starts drawing. After the first line in the above fragment is executed, the following drawing results:
The box on the left is not an object, rather it is the way we draw a sheet of scratch paper. We've titled the piece of paper to differentiate it from other sheets of paper we'll be adding to this picture later on. In this case, we've chosen the arbitrary name "fragment" for our sheet of scratch paper. We modify the picture after executing the second line:
Now, things get interesting. When a message send takes place, we get a
new piece of scratch paper, title it with the name of the message, and
start drawing on that sheet of paper. Remember, when a method is
invoked, parameter names are bound to the corresponding argument
values, so we need to show those names on our paper. When control
passes to the transferFrom
method, the following picture
results:
Notice that we've written three names: this
(referring to
the first bank account), other
(referring to the second
bank account), and amount
(referring to the value 75.0).
We show the binding for this
to denote the object that is
receiving the message.
Remember that the first thing that the transferFrom
method
does is call another method, withdraw
. Again, this means
adding another piece of scratch paper to our picture. When control
passes to the withdraw
method, the following picture
describes the state of the world:
It's starting to look a bit messy, but we've really just added another
piece of paper, and a couple of names to our world. Notice that on
this new piece of paper, the name this
refers to a
different object than the name this
on the piece of paper
for tranferFrom
. That makes sense, because each of those
message sends have different receivers. Writing down which object
this
refers to helps us keep straight which object is
handling a given message.
Now imagine that the withdraw
method finishes. What's
next? Well, control returns to the call site -- in the body of the
transferFrom
method. At this point, the piece of paper
for withdraw
is thrown away, and the following picture
results. You've seen it before, except that the second bank account
is missing some money -- due to the impact of the withdrawal we just
made.
The next line of the body of transferFrom
sends the
deposit
message to this
. Again, we modify
our picture to reflect the state of the world when that method is invoked:
Of course, the impact of the deposit method is just to increase
the balance of the first account by $75.00. This happens, and the
piece of scratch paper for this method is thrown away, and control returns
to the transferFrom
method again:
At this point, there's nothing left to do in the transferFrom
method, so we just throw its piece of paper away, resulting in the
final state of the world: