Lesson 3.3 Expressions
Operators & Expressions
Now that you've learned to create numeric and character variables, it's time to turn our attention to the second part of the "primitive puzzle."
You recall that primitives, unlike objects, are manipulated via operators, instead of methods. Such manipulations are called expressions, and are the subject of this lesson.
Expression Basics
What do we mean by "an expression"? Here's a formal definition:
"An expression is any combination of operators and operands which, when evaluated, yields a value"
Well, that's not very helpful; what is an operand? What is an operator? What is evaluation? What does this mean? Let's look.
Operands
An operand is simply a symbol that represents a value or storage location in memory. Operands include
- Literals [which represent a value] Variables [which represent a storage location] Method invocations [which can produce a value]
- Subexpressions [the result produced by an expression]
Operators
An operator is a symbol that is used to perform an operation on an operand and produce a value. Operators have three important characteristics:
- Arity: the number of operands required. Some operators require only a single operand; we call these unary operators. Most operators require two operands; we call these binary operators. Finally, one Java operator requires three operands; the ternary conditional operator.
- Precedence: this characteristic helps determine the order in which operations involving multiple operators are performed. Operations that have higher precedence are performed before operations that have lower precedence. [This is slightly different than C and C++]
- Associativity: this characteristic determines whether operations involving multiple operators at the same level of precedence should proceed from right-to-left, or from left-to-right.
Numeric Operators
Here are some basic numeric operators that can be applied to any numeric type. Each of these is a binary operator:
- + Addition
- - Subtraction or unary minus
- * Multiplication
- / Division (both integer and real)
- % Remainder (also works with floats)
Expressions
When operators and operands are combined, each operator is applied to its operands and a temporary value is produced. Here's an example:
This expression uses two literal operands as well as the addition operator. This expression produces the value 13. The value produced by the expression can be used wherever a literal value can be used. For instance, both of these declarations put identical values into their respective variables:
int x = 13;
int y = 10 + 3; |
Much of the time, a temporary value is all you need. If you need to store the result of an expression, however, you can use Java's assignment operator to copy the temporary value into a variable.
Assignment
In Java, assignment is an operator, not a statement like it is in Pascal or Basic. [Assignment acts very similar to assignment in C, except the result of the assignment cannot be used as a true/false value unless you are assigning to a boolean variable.]
Assignment is a binary operator whose operation is to:
"Copy the value on the right into the variable on the left, and return the result copied as its value "
This means that, for the assignment operator, the operand on the left must be a variable; it cannot be a constant or a method invocation.
|
Assignment is Not Equality
|
| You might find this confusing because in math the "equals" sign does not mean copy, instead it means that the value on the left and the value on the right are identical.
Here's an example. In Algebra, you could never say
x = x + 1
because for every value of x, x + 1 is never equal to x. In Java, however, this simply means to add x and 1 together, and then store the result of the operation back into the variable x.
This works both ways. In Algebra, the following is legal
13 = a + b
but it is illegal [and nonsense] in Java, because 13 is not a variable that can be changed. |
The Value of Assignment
The assignment operator not only copies the value on its right into the variable on its left, but it also produces a value; the value produced by an assignment is the value copied into the variable on the left.
| Technically the fact that the variable on the left is modified by the assignment operation is considered a side effect. Of course, most of us use the assignment operator only for this side effect and don't give much thought to the value that is produced. |
Because assignment first examines the expression on its right before copying it to the variable on its left, we say that assignment is right associative. This simply means it works from right to left.
Chained Assignment
Because of that, we can chain assignment statements together like this:
This expression first applies the assignment operation to the variable z, copying the value 10 into the variable. The expression z = 10 produces a value (10), which is used as the right-hand operand in the next assignment, where 10 is copied into y. In the same way, assigning 10 to y produces a value (10), and that value is used as the right-hand operand for the final assignment to x. There is one potential pitfall you may encounter when doing this: it does not work when defining a new variable, only on variables that already exist. That means you cannot write:
| int x = y = z = 10; // Not OK |
Numeric Operations
As you saw earlier, Java has the following five numeric operators that work on both integers and floating point numbers [or a combination of the two].
- + Addition
- - Subtraction or unary minus
- * Multiplication
- / Division (both integer and real)
- % Remainder (also works with floats)
Here are some integer variables and numeric expressions. What values do you suppose each of the following variables will hold?
| int a = 7, b = 12;
int diff = b - a;
int product = b * a;
int quotient = b / a;
int remainder = b % a;
|
As you might expect, the variable diff will hold the value 5, the variable product will hold the value 84.
Integer Division
You might be surprised to find that the variable quotient holds the value 1, and not the fractional number 1.74. Because both a and b are integers, Java performs integer division when it encounters the / operator.
Integer division works like the long division you learned in third grade.
- Only the quotient is calculated any remainder is discarded. The result is not rounded.
- The result is, itself, an integer value.
The Remainder Operator
Rather than throwing away the remainder calculated by an integer division, you can use the remainder operator [sometimes called the modulus operator] to calculate it separately.
In our example, the variable remainder will have the value 5, because 7 "goes into" 12 once, with 5 left over. Here are a couple "challenge questions" you can practice with. See if you can figure out what the answers should be. You can find the answers at the very bottom of the page.
int a = 5 % 3;
int b = 0 % 2;
int c = 12 % 12;
int d = 2 % 0; |
Increment and Decrement
One common operation in most computer programs is to add or subtract one from a variable and then store the result back into the variable. This is called incrementing or decrementing the variable.
This is easily done using the operators you've met already [addition, subtraction, and assignment] like this:
int a = 5;
...
// Inside a method
a = a + 1;
a = a - 1;
|
One problem with this solution is that the expression a + 1 produces a temporary value [like all expressions] that must be stored before it is finally copied back into the variable a by the assignment operator. Since most computer hardware contains special instructions to add one to, [or subtract one from], a variable "in place" [that is, without creating a temporary variable], Java provides operators that use these more efficient instructions.
The Operators
The increment [++] and decrement [--] operators are unary operators that can only be applied to a variable. When you use the increment operator like this
the value that was, previously, stored in a is incremented by 1. You could not write
because 10 is not a variable. Since the increment operator modifies its operand, like the assignment operator, this behavior is a side effect. In addition to this side effect, the increment operator also produces a value.
Pre and Post
Even though increment and decrement are unary operators, they can be used in two different ways: they can be placed before or after a variable like this:
When placed before the variable, it is called a pre-increment [or decrement] expression. When placed after the variable, it is called a post-increment [or decrement] expression. As far as the side effect is concerned it doesn't make a bit of difference whether the operator is placed in front of, or after, a variable. In both cases, the variable is left holding a value one greater [or less] than it was before. The value produced by an increment or decrement, however, depends on whether it is a post or pre expression. In pre-increment [decrement], the variable is first modified and the result of the expression is the new value. In post-increment [decrement], the value of the expression is the value the variable had before it was changed. This can be confusing, so here are a couple examples:
int c = 5;
int d = 5;
int a = ++c;
int b = d++; |
After these operations, the variables c and d both have the value 6. Changing c and d is a side effect of the increment and that remains the same whether you use pre or post increment. The variable a also has the value 6, because the expression ++c means to first change c and then produce the changed value. The variable b, however has the value 5, because the expression d++ means "change the value of d, but produce the value that d had before the change."
Shorthand Assignment
Much of the time, when you perform an operation on a variable, you store the result of that operation right back in the same variable, much like with incremeting and decrementing. If you are summing up the total in a checkbook, for instance, this would be a common expression:
| balance = balance - checkAmount; |
Since what we really want to do is just subtract checkAmount from balance, the shorthand-assignment operators give us a concise way to do that. Using these operators we could write the previous expression as:
There are shorthand assignment operators corresponding to most of the basic numeric operators. Here is what each of them mean:
|
Original
|
Shorthand
|
| a = a + b |
a += b;
|
| a = a - b; |
a -= b;
|
| a = a / b; |
a /= b;
|
| a = a * b; |
a *= b;
|
| a = a % b; |
a %= b;
|
String Operations
Although Strings are really reference types, like other objects, there are also have several operators that act upon String operands.
When used with Strings, the plus sign [+] acts to "paste" two Strings together to form a new String, a process called concatenation. In addition, the shorthand operator [+=] also performs concatenation when its left-hand operand is a String variable. Here's are some examples of String concatenation:
String s1 = "How";
String s2 = "now";
String s3 = "cow?";
String s4 = s1 + " " + s2;
s4 += " brown " + s3;
|
Note that you can concatenate both variables and String literals. You can use the += operator only when the value on the left is a String variable, as in the last line.
Precedence and Associativity
When we first looked at operators, we saw that they had the characteristics of arity, precedence, and associativity.
Precedence is one of the rules that determines the order in which operators are applied to operands. Higher-precedence operators get to perform their operations before those of lower caste. Here is a simplified precedence table [covering the basic opertors] used in Java:
As you can see, the unary operators have the highest precedence, followed by the multiplication operators, addition, and finally assignment. When faced with an expression involving multiple operations, you use the rules of precedence to decide "who goes first." Here's an example:
There are obviously several ways you could evaluate this expression. One way would be to go from left to right so that:
3 + 4 = 7
7 * 7 = 49
49 - 9 = 40 |
Applying the rules of precedence, however, you can see that multiplication [*] has higher precedence, so you perform that first, followed by the addition and subtraction:
4 * 7 = 28
28 + 3 - 9 = 22 |
Quite a difference!
Associativity
Suppose that our previous expression had read like this instead:
Now we have a quandry. Both multiplication and division have the same precedence. But if we do the multiplication first we get this:
4 * 7 == 28
28 / 9 == 3
3 + 3 == 6 |
If we do the division first, we get this:
7 / 9 == 0 //[Integer division]
4 * 0 == 0
3 + 0 == 3 |
Which should we use? In situations like this, where precedence offers insufficient guidance, we have to fall back on the associativity of the operators. Remember that associativity determines whether an operation is performed left-to-right or right-to-left. The unary operators and the assignment operator work right-to-left [they are right-associative]. All of the other operators work left-to-right. Associativity is only needed to act as a "tie-breaker" when operators share operands at same level of precedence. If operators are at different levels of precedence, you don't need to consult the operator associativity.
Mixed-Type Operations
Not all expressions involve integers. You can also have expressions using floating-point numbers, characters, and Strings. You can even have expressions that involve all of the above.
Remember, every expression produces a value. Each value produced by an expression has a particular type. When you add or subtract two integers, for instance, the result of that expression is an integer. When you add or subtract two floating-point numbers, the result is a floating point number. If you perform an operation with a character, the result is a character, and with a String, the result is a String. Simple. But, what happens when you write this?
Whoa! Now we've got a problem. You know that the literal 5 is an int. You also know that the floating-point literal 3.0 is a double [because it doesn't have the F suffix]. What you don't know is the result of multiplying 5 * 3.0, or what type the variable a is. Let's look at both those questions.
The Data Hierarchy
Not all numeric types are created equal. A short can hold more information [a greater range of values] than a byte, and an int can hold more information than a short. Likewise, a double can hold more information than a float--it is more precise.
When Java encounters an expression that uses different types of operands, it first determines the most precise of the operands using this data hierarchy. Then, it creates temporary values of the most precise type, intitializing those temporary values using the less-precise values. Let's look at our example again. Of our two values, 5 and 3.0, 3.0 has greater precision--it is higher in the data hierarchy. To perform the calculation, Java creates an unnamed temporary double value that contains 5.0, to take the place of the int value 5 during the calculation. The int value 5 is not changed in any way by this. Finally, the multiplication is performed using the double values 3.0 and 5.0 and the result is a double value 15.0.
Conversion and Assignment
Operations like mixed-type multiplication and addition are pretty straightforward, but what about assignment? What value is stored in the variable a?
When it comes to mixed-type arithemtic, assignment is special among the operators, because Java will not allow you to store a "more precise value" in a "less precise" bucket. What this means for our example is that Java will refuse to compile it unless the variable a is a double variable. If a were an int or a long or even a float, information would be lost by storing the double value 15.0 in the less precise variable. On the other hand, if the variable is of a higher [more precise] type than the value being stored, Java will perform the assignment without so much as a pause. For instance,
is perfectly OK because the less-precise float value is easily stored in the more-precise double variable. However, the reverse is not true. The expression
will not compile because the literal 123.45 is a double and Java will not store it in a float variable. Here's another, similar example, using integers:
int x = 123;
byte b = x; // Not OK
long l = x; // OK |
Primitive Casts
While we all appreciate Java's efforts to protect our data, sometimes we really do want to store the value 15.0 in an int, or even a byte variable. When you want to do this, you need to perform a type cast, usually shortened to cast.
A cast tells Java to "try" to store the value, despite its reservations. The syntax for the cast operator is:
Java creates a temporary unnamed variable when you do this; it does not change original-value in any way. Here's an example that uses a type-cast to assign the int value 123 to a byte variable:
int x = 123
byte b = (byte) x; // OK |
The problem with casts is that they always "succeed" in the sense that Java will store something. It may not have the effect you desire, however. Here are two examples. Can you tell what happens?
byte b = (byte) 256; // OK???
int n = (int) .999; // OK??? |
In the first example, the result is zero, because the bit pattern for the int 256 is 0000-0000-0000-0000-0000-0001-0000-0000, but when stored in a byte, only the right-most eight bits are used. The bit in position 8, [numbered starting at zero from the right], is simply discarded. A similar situation occurs when you attempt to cast the int value 250 to a byte. Although a byte contains enough bits to represent the number 250 as an unsigned number, as a signed number it doesn't, so the cast results in a negative number, as you can see here. Finally, in the last example, the double value .999, when converted to an int is zero, because an int is incapable of representing a fractional number.
Something to Talk About
Do you understand Java expressions? Here are several questions you can try to make sure you understand how to evaluate Java expressions:
- In the section entitiled Associativity, an expression was presented, but two differing solutions were given. Which one is correct?
- What is the value of the following expression?
- What is the value and type of the following expression?
- What is the value and type of the following expression?
- What is the value of b at the end of the following set of operations?
int c = 3;
int b = c--;
++b; |
Answers to Challenge Questions
The Remainder Operator
int a = 5 % 3; // 2
int b = 0 % 2; // 0 [No 2s in 0, so no remainder]
int c = 12 % 12; // 0
int d = 2 % 0; // Illegal. Cannot divide 2 by 0
Please continue to the next section of this lesson.
|