Incremental Java
Assignment Statements

Writing to a Box, Again

Remember when you learned about boxes and values? What were the two operations you could perform on a box? You could either read from a box or write to a box.

Expression evaluation usually reads from a box (sometimes it reads and writes, but usually reads).

The only way you know how to write to a box is to use an initializer. Unfortunately, you can only use an initializer when you are declaring a variable, and you can only declare a variable once (within a scope).

We need a way to change the value of the box. Redeclaring a variable is invalid. For example,

  int x = 10 ;
  int x = 20 ; // WRONG!  Can't declare "x" a second time
It seems like we're stuck. Of course, we aren't. We can use assignment statements.

Assignment Statements

An assignment statement looks a lot like a declaration with an initializer.

Here's an example:

  isSwitchOn = true ;
The syntax of the assignment statement is:
  lvalue = rvalue ;
lvalue is short for "left value", and refers to something that can be assigned to. Right now, the only thing we can assign to is a variable.

The equal sign is not really an equal sign. I mean, yes, you type the equal sign, but it doesn't stand for equality. It is called the assignment operator.

rvalue is short for "right value". This can be any expression, as long as the type is compatible with the type of the lvalue. It's always safe to make the types of the lvalue and the rvalue match. However, Java does permit the types to be different, in special circumstances. We'll discuss this at a later time.

Semantics

What does an assignment statement do? In programming, we use a special word called semantics to refer to meaning. I'm going to explain the semantics of the assignment operator.

  1. Evaluate the right hand side (which we write RHS, for short).
  2. Write this value in the box on the left hand side (which we write LHS, for short)
The RHS is an expression, which can be evaluated to a value. The LHS is treated as a box, and we put the value in the box.

This is very interesting, once you think about it. If a variable cost appears on the right hand side, then we read the value from the box (i.e., take the value out of the box). Suppose cost held the value 10. We could either use cost or 10 in the expression on the RHS, and get the same evaluated value.

Here's an example:

  total = cost + tax ;
We can plug in 10 for cost (assuming that's the value of cost) and write this as:
  total = 10 + tax ;
This would be the same as before (at least, for this particular value of cost).

However, if cost is on the LHS, then we don't care about the value in the box. We intend to write a new value to replace it. In this case, it's very important that we have the box. We can't get equivalent behavior by replacing a variable with its value.

For example, we can't rewrite:

  cost = 10 * 2 ;
as:
  10 = 10 * 2 ;
It doesn't make sense to replace cost with its value, since we really need the box associated with cost, not the value inside.

To summarize, when a variable appears on the RHS, we want its value. We read it from the box. When it appears on the LHS, we want the box itself so we can write to it.

Assignment Isn't Equality

Suppose cost holds the value 10. What is the result of the following assignment statement?
  cost = cost + 1 ;
If you've never done any programming, this would seem strange. How can a number equal itself plus 1?

However, = is not equality. That's == (two equal signs). It's the assignment operator. We just said this operator evaluate the RHS, then writes the value to the LHS.

Let's see what happens.

Is Assignment an Expression?

Yes, assignment is an expression. This means you can get a value. For example, consider the following code:
  int x, y, z ;

  x = (y = 3) + (z = 4) ;
We use (y = 3) as an expression. It's an unusual expression. First, it evaluates to whatever y is assigned to (in this case, 3). Second, it changes the value of y.

This is called a side effect. Normally, evaluating expressions is side-effect free. Usually, no variables are written to. However, with assignment statements, the variable is assigned to, so it is written. A side effect occurs when there's some left over effect beyond the computation. In this case, the left over effect is modifying a variable's value.

In the assignment statement above, (y = 3) + (z = 4) evaluates to 3 + 4 which is 7. Notice that we don't follow the usual rules of evaluating expressions. In particular, we don't plug in values for y and x in the expression.

7 is assigned to x which means x now has the value 7.

The above assignment statement is particularly weird. You wouldn't see many programmers write the code. I only write it to show that assignment statements are expressions too (i.e., they can be evaluated to a value, and can be used as part of a larger expression). In some languages (like Pascal), assignment statements are not expressions. These languages would forbid an assignment statement as shown above.

Copying Handles

Look at this piece of code.
  int x = 2, y ;

  y = x ; // y gets a copy of x
Both y and x contain the value 2. However, each has its own copy. If you assign y a new value, x is still 2, and it remains 2 until x is assigned a new value.

When you assign one variable to another, you are simply copying the value from one box to another. That is, you are reading from the box on the RHS, and then writing that value to the LHS.

This is the semantics for assigning primitive variables.

What if x and y were objects? What if they were String objects?

  String s1 = "cat", s2 ;

  s2 = s1 ; // s2 gets a copy of s1 handle
In the declaration, "cat" is constructed. This creates a balloon (which contains the string) and a handle which is use to initialize s1.

In the assignment, s2 = s1, you evaluate the RHS. Evaluating an object variable "reads" the value from the box, but for object variables, you get a handle back. When you assign it, you copy this handle to s2.

Now we have two handles, but only one balloon. Both handles are attached to the same balloon.

If Java permitted Strings to be changed (it doesn't), and we changed the string from "cat" to "cart" using s1, then s2 would also see this change. That's because s2 only has a copy of the handle, not a copy of the object.

What if we really wanted to make a copy of the object? Java has a way of doing this, but we'll come back to it later, once we learn more about objects. For now, realize that Java programmers primarily use assignment operators to copy handles.

Here's another interesting piece of code.

  String s1 = "cat", s2 ;

  s2 = s1 ; // s2 gets a copy of s1 handle
  s1 = "slime" + s1 ; // Hmmm....
What does the second assignment statement do? As usual, we evaluate the RHS. We evaluate s1, which gives us a handle. We evaluate "slime" which constructs a String and also gives us a handle.

Java can figure out that the + operator is the string concatenation operator by looking at the type of the left and right operand (both are Strings).

Concatenation constructs a new string. In this case, the new string is "slimecat". This is a new balloon. A new handle is also created. Thus, s2 has a handle to "slimecat" while s1 has a different handle to "cat".

As you can see, dealing with objects can get tricky. You have to figure out whether an object is being modified, but you still have the same handle, or whether the operator creates a new handle.

Summary

Assignment statements are used to assign new values to variables. Variables can not be assigned to, unless they have been previously declared (in a scope).

The semantics of an assignment statement is similar to a declaration with an initializer. Evaluate the RHS, and write that value to the variable on the LHS.

The difference between an assignment statement an a declaration is that a declaration creates a box. An assignment statement reuses the box created by the declaration.

If you're doing a simple assignment of the form x = y, then if x and y are primitive variables, then this copies the value of y to x. If they are object variables, it copies the handle of y to x (or copies null is y does not hodl a handle). When you copy a handle, both x and y now have a handle to the same balloon. In particular, x now shares a handle to the balloon that y was holding.