How Expressions Are Evaluated

   

When an expression is simple, like those shown in Table 5.1, figuring out its evaluated result is easy. When expressions become more complex and multiple operators are used, following what they do gets a little more complicated.

The examples of expressions in Chapter 3 showed you that expressions are just combinations of operators and operands. And although that definition is true, it's not always very helpful. Sometimes you need to create and use complex expressions ”maybe to perform a complicated calculation or some other involved manipulation of multiple variables . To do this, you need a deeper understanding of how Java expressions are created and evaluated. In this section, you look at three major tools that will help you in your work with Java expressions: operator associativity, operator precedence, and order of evaluation.

Operator Associativity

The easiest of the expression rules is associativity. All the arithmetic operators are said to associate left to right. This means that if the same operator appears more than once in an expression ”such as the plus sign in a+b+c ” then the leftmost occurrence is evaluated first, followed by the one to its right, and so on. Consider the following assignment statement:

 x = a+b+c; 

In this example, the value of the expression on the right of the = operator is calculated and assigned to the variable x on the left. In calculating the value on the right, the fact that the + operator associates left to right means that the value of a+b is calculated first, and the result is then added to c. The result of that second calculation is assigned to x. So if you were to write it using explicit parentheses, the line would read:

 x=((a+b)+c); 

The concept of associativity holds true for the other arithmetic operators in the same way. You would use the associativity rule in evaluating the right sides of each of the following assignment statements:

 volume = length * width * height; orderTotal = subTotal + freight + tax; subTotal = orderTotal  freight  tax; perOrderPerUnit = purchase / orders / units; 

You probably remember from your early math classes that multiplication and addition are associative operations. This means that the order of the two operands used with either of these operators can be switched without affecting the result of an expression. Simply put, a+b is the same as b+a and c*d is the same as d*c. Subtraction and division are not associative however. Clearly, a “b and c/d are not the same as b “a and d/c. With this in mind, expressions that use subtraction and division rely on a correct ordering of operands to produce a correct result. Using the example for perOrderPerUnit, this expression would be evaluated as

 (purchase / orders) / units 

If the intent, however, is to get the result of

 purchase / (orders / units) 

the expression must be written as

 purchase / orders * units 

or parentheses must be added to control the order of evaluation. As with other languages, operations inside parentheses in Java are evaluated prior to being used within a surrounding expression.

Note

Unlike the arithmetic operators, the unary and assignment operators associate right to left. This means that it is possible to chain assignment statements such as a=b=c where values are successively assigned to the operand to the left of the rightmost = operator.


Operator Precedence

When you have an expression that involves more than one type of operator, the associativity rule isn't enough to determine the order of evaluation. Now the precedence, or relative priority, of operators must be considered also.

Precedence determines which operator acts first in an expression. If you wanted to evaluate A+B*C, by standard mathematics you would first multiply B and C and then add the result to A. Operator precedence accomplishes the same thing in your programs. The multiplicative operators ( *, /, and % ) have higher precedence than the additive operators ( + and - ). So, in a compound expression that incorporates both multiplicative and additive operators, the multiplicative operators are evaluated first.

Consider the following assignment statement, which is intended to convert a Fahrenheit temperature to Celsius:

 Celsius = Fahrenheit - 32 * 5 / 9; 

To correctly convert between these two scales , you take a temperature measured in degrees Fahrenheit, subtract 32 from it, multiply the result by 5, and divide that result by 9. However, because the * and / operators have higher precedence than the - operator, the subexpression 32 * 5/9 would be evaluated first (yielding the result 17) in the preceding equation. That value would then be subtracted from the Fahrenheit variable giving an incorrect result.

The solution to problems like this is a simple one. As in the case of associativity, you can use parentheses to control the order of evaluation of operators in an expression. To perform the correct conversion for the preceding example, you would write the equation as:

 Celsius = (Fahrenheit - 32) * 5 / 9; 

Interestingly, there are some computer languages that do not use rules of precedence. Some languages, such as APL for example, use a straight left-to-right or right-to-left order of evaluation, regardless of the operators involved.

Use of parentheses also helps the following examples be evaluated correctly:

 newAmount = (savings + cash) * exchangeRate; totalConsumption = (distance2  distance1) * consumptionRate; 

Although the multiplicative operators have high precedence, the unary operators, which are those that act on a single operand, take precedence over even them. In the following example, you multiply the value - 5 times the value of height instead of multiplying 5 times height and negating the result (although the results are the same in this case):

 offset = -5 * height; 

Table 5.2 is what is known as the precedence table. The operators with the highest precedence are at the top. Operators on the same line are of equal precedence.

The majority of these operators associate left to right. The exceptions to this rule are the unary operators, assignment operators, and the conditional operator. For any single operator, operand evaluation is strictly left to right, and all operands are evaluated before operations are performed. The implications of this are explained in the next section.

Table 5.2. The Java Operator Precedence Table
Description Operators
Postfix Operators . [] () expr++ expr “ “
Prefix Unary Operators + “ ~ ! ++expr “ “expr
Creation or Cast new (type)expr
Multiplicative * / %
Additive + “
Shift << >> >>>
Relational < <= >= > > instanceof
Equality == !=
Bitwise AND &
Bitwise XOR ^
Bitwise OR
Conditional-AND &&
Conditional-OR
Conditional ?:
Assignment = operator=

Order of Evaluation

Many people, when they first learn a language, confuse the issue of operator precedence with order of evaluation. The two are actually quite different. The precedence rules help you determine which operators act first in an expression. This effectively determines what the operands are for a specific operator. For example, in the following line of code, the operands of the * operator are a and (b+c):

 d = a * (b+c); 

The order-of-evaluation rules, on the other hand, help you to determine not when operators are evaluated, but when operands are evaluated. Now, with a simple operand such as a, the concept of evaluating an operand doesn't seem to mean much. In this case, the operand evaluates to the value of the variable a. The operand (b+c) is more interesting, but still simple. First, b and c are operands of the + operator, so they must be evaluated to their corresponding values. These values are then added to produce the evaluated result of (b+c). At this point, with the values of both operands known, the * operator can be invoked.

Here are three rules (in no particular order) that should help you remember how an expression is evaluated:

  • For any binary operator (one with two operands), the left operand is evaluated before the right operand. In the preceding example, a is evaluated before (b+c).

  • Operands are always evaluated fully before the operator is evaluated. Again, a and (b+c) are evaluated before any multiplication takes place.

  • If a number of arguments are supplied in a method call separated by commas, the arguments are evaluated strictly left to right.

The following shows how this works with something more complicated. This isn't an example of good, or even acceptable programming style, but see if you can correctly evaluate the results of the following code segment.

 int[] x = { 0,0,0} ; int y = 1; int z = 3; x[y++] = y = z * y; 

This might look more like a compiler error to you than a useful example, but the rules already covered here enable you to work this out. After this code is executed, both y and x[1] are equal to 3 (the other elements of x are still 0).

The way to approach a statement like this is to divide and conquer. Without any paren theses present, first determine what the operands are for each operator. Given that unary operators take precedence over multiplication, and multiplication takes precedence over assignment, this determination is actually straightforward. The operands of the * operator are z and “ “y. Assignment is performed from right to left, so the second = operator is assigning z * “ “y to the variable y. The first = operator is then assigning this value of y to x[y++]. The equation could then be viewed as

 x[y++] = (y = (z * (y))) 

Now it gets tricky. The important point to remember here is that although operators in an expression act according to precedence, the individual operands are evaluated from left to right. This matters most in a case like this where operands that produce side effects ( ++ and “ “ ) are present. So, with this rule in mind, the first step is to evaluate x[y++]. Because a post-decrement is being used, this operand evaluates to x[1] and then has the side effect of incrementing y to 2 afterward. The operand y (to the left of the second = operator) evaluates to the variable y. That leaves you with (z * (“ “y)). The operator precedence has already been represented by adding the parentheses, so the first step is to decrement y. This occurs before the operand is evaluated this time because it's a pre-decrement. Because y was already incremented to 2, this takes it back down to 1. The z * “ “y operand is then fully evaluated as 3 * 1 or 3. The assignment operations are then performed, so y is assigned this value of 3 and x[1] is assigned this new value of y.

Go through this example until each step makes sense to you. Acquiring a solid understanding of these rules early on will reduce your chances of being surprised by an unexpected evaluation of a statement deep within your code someday. If nothing else, you will gain an appreciation for programmers who use parentheses to make their expressions clear.

   


Special Edition Using Java 2 Standard Edition
Special Edition Using Java 2, Standard Edition (Special Edition Using...)
ISBN: 0789724685
EAN: 2147483647
Year: 1999
Pages: 353

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net