Section 2.7
Details of Expressions


THE REST OF THIS CHAPTER COVERS the details of certain aspects of Java that have already been introduced earlier in the chapter. This section covers expressions. Recall that an expression computes a value. The value can, for example, be assigned to a variable, used as the output value in an output routine, or combined with other values into a more complicated expression. If the value is a boolean value, then it can be used as the test condition in a while loop or an if statement. (The value can even, in some cases, be ignored, if that's what you want to do; this is more common than you might think.)

The basic building blocks of expressions are literals (such as 674, 3.14, true, and 'X'), variables, and function calls. Recall that a function is a subroutine that returns a value. You've already seen some examples of functions: the input routines from the console class. Java also has some built in mathematical functions for computing things like square roots and logarithms. The mathematical functions are part of a class named "Math", and so they have compound names beginning with "Math", such as Math.sqrt for the square root function. For example, to compute the square root of x and assign the answer to y, you would say:

y = Math.sqrt(x);

Here is a list of the most important mathematical functions defined in the Math class:

For these functions, the type of the argument -- the value inside parentheses -- can be of any numeric type. For most of the functions, the value computed by the function is of type double no matter what the type of the argument. However, for Math.abs(x), the value returned will be an integer if x is an integer. (This means that Math.sqrt(9) is the double value 3.0, but Math.abs(9) is the int value 9.) Note that Math.random() does not have any argument. You still need the parentheses, even though there's nothing between them. The parentheses let the computer know that this is a subroutine rather than a variable.


Literals, variables, and function calls are simple expressions. More complex expressions can be built up by using operators to combine simpler expressions. Operators include + for adding two numbers, < for comparing two values, and so on. When several operators appear in an expression, there is a question of precedence, which determines how the operators are grouped for evaluation. For example, in the expression "A + B * C", B*C is computed first and then the result is added to A. We say that multiplication (*) has higher precedence than addition (+). If the default precedence is not what you want, you can use parentheses to explicitly specify the grouping you want. For example, you could use "(A + B) * C" if you want to add A to B and then multiply the result by C.

The rest of this section gives details of operators in Java. The number of operators in Java is quite large, and I will not cover them all here. Most of the important ones are here; a few will be covered in later chapters as they become relevant.


Arithmetic Operators

Arithmetic operators include addition, subtraction, multiplication, and division. They are indicated by +, -, *, and /. These operations can be used on values of any numeric type: byte, short, int, long, float, or double. When the computer actually calculates one of these operations, the two values that it combines must be of the same type. If your program tells the computer to combine two values of different types, the computer will convert one of the values from one type to another. For example, to compute 37.4 + 10, the computer will convert the integer 10 to a real number 10.0 and will then compute 37.4 + 10.0. (The computer's internal representations for 10 and 10.0 are very different, even though people think of them as representing the same number.) Ordinarily, you don't have to worry about type conversion, because the computer does it automatically.

When two numerical values are combined (after doing type conversion on one of them, if necessary), the answer will be of the same type. If you multiply two ints, you get an int; if you multiply two doubles, you get a double. This is what you would expect, but you have to be very careful when you use the division operator /. When you divide two integers, the answer will always be an integer; if the quotient has a fractional part, it is discarded. For example, the value of 7/2 is 3, not 3.5. If N is an integer variable, then N/100 is an integer, and 1/N is equal to zero for any N greater than two! This fact is a common source of programming errors. You can force the computer to compute a real number as the answer by making one of the operands real: To compute 1.0/N, for example, the computer converts N to a real number in order to match the type of 1.0, so you get a real number as the answer.

Java also has an operator for computing the remainder when one integer is divided by another. This operator is indicated by %. If A and B are integers, then A % B represents the remainder when A is divided by B. For example, 7 % 2 is 1, 34577 % 100 is 77, and 50 % 8 is 2. In the previous section, I used the expression N % 2 to compute the remainder when N is divided by 2. This remainder will be 0 if N is even and will be 1 if N is odd.

Finally, you might need the unary minus operator, which takes the negative of a number. For example, -X has the same value as (-1)*X. For completeness, Java also has a unary plus operator, as in +X, even though it doesn't really do anything.


Increment and Decrement

You'll find that adding 1 to a variable is an extremely common operation in programming. Subtracting 1 from a variable is also pretty common. In the examples in previous sections, you've already seen the statements:

                    years = years + 1;
                    counter = counter + 1;

The operation of adding 1 to a variable named x can be accomplished by writing x++ (or, if you prefer, ++x). This actually changes the value of x, so that it has the same effect as writing "x = x + 1". The two statements above could be written

                    years++;
                    counter++;

Similarly, you could write x-- (or --x) to subtract 1 from x. Adding 1 to a variable is called incrementing that variable, and subtracting 1 is called decrementing. The operators ++ and -- are called the increment operator and the decrement operator, respectively. These operators can be used on variables belonging to any of the numerica types and also on variables of type char.

Usually, when you use ++ or --, you write statements like "x++;" or "x--;". These statements are commands to change the value of x. However, it is also legal to use x++, ++x, x--, or --x as expressions, or as parts of larger expressions. That is, you can write things like:

                    y = x++;
                    y = ++x;
                    console.putln(--x);
                    z = (++x) * (y--);

The statement "y = x++;" has the effects of adding 1 to the value of x and, in addition, assigning some value to y. The value assigned to y is the value of the expression x++, which is defined to be the old value of x, before the 1 is added. Thus, if the value of x is 6, the statement "y = x++;" will change the value of x to 7 and will change the value of y to 6. On the other hand, the value of ++x is defined to be the new value of x, after the 1 is added. So if x is 6, then the statement "y = ++x;" changes the values of both x and y to 7. The decrement operator, --, works in a similar way.

This can be confusing. My advice is: Don't be confused. Use ++ and -- only in stand-alone statements, not in expressions.


Relational Operators

Java has boolean variables and boolean-valued expressions that can be used to express conditions that can be either true or false. One way to form a boolean-valued expression is to compare two values using a relational operator. Relational operators are used to test whether two values are equal, whether one value is greater than another, and so forth. The relation operators in Java are: ==, !=, <, >, <=, and >=. The meanings of these operators are:

              A == B       Is A "equal to" B?
              A != B       Is A "not equal to" B?
              A < B        Is A "less than" B?
              A > B        Is A "greater than" B?
              A <= B       Is A "less than or equal to" B?
              A >= B       Is A "greater than or equal to" B?

These operators can be used to compare values of any of the numeric types. They can also be used to compare values of type char. For characters, < and > are defined according the numeric Unicode value of the characters. (This might not always be what you want. It is not the same as alphabetical order because all the upper case letters come before all the lower case letters.)

When using boolean expressions, you should remember that as far as the computer is concerned, there is nothing special about boolean values. You can use boolean expressions in while statements and if statements, of course, but you can also assign them to boolean variables, just as you can assign numeric values to numeric variables. A boolean variable can itself be used as a boolean expression. For example,

             console.put("Enter a positive number: ");
             int N = getInt();
             boolean badInput = (N <= 0);
             while (badInput) {
               console.put("I said a POSITIVE number: ");
               N = getInt();
               badInput = (N <= 0);
             }

Using the boolean variable is a bit silly here, but if the condition that you want to test is very complicated, you might want to do some fancy computations and store the result in a boolean variable.

By the way, the operators == and != can be used to compare boolean values. This is occasionally useful. For example, can you figure out what this does:

boolean sameSign = ((x > 0) == (y > 0));


Boolean Operators

In English, complicated conditions can be formed using the words "and", "or", and "not." For example, "If there is a test and you did not study for it...". "And", "or", and "not" are boolean operators, and they exist in Java as well as in English.

In Java, the boolean operator "and" is represented by &&. The && operator is used to combine two boolean values. The result is a boolean value that is true if both of the combined values are true, and the result is false if either of the combined values is false. For example, "(x == 0) && (y == 0)" is true if and only if both x is equal to 0 and y is equal to 0.

The boolean operator "or" is represented by ||. (That's supposed to be two of the vertical line characters, |.) "A || B" is true if either A is true or B is true, or if both are true. "A || B" is false only if both A and B are false.

The operators && and || are said to be short-circuited versions of the boolean operators. This means that the second operand of && or || is not necessarily evaluated. Consider the test

if ( (A != 0) && (B/A > 1) )

Suppose that the value of A is in fact zero. In that case, the division B/A is illegal, since division by zero is not allowed. However, the computer will never perform the division, since when the computer evaluates (A != 0), it finds that the result is false, and so it knows that (A != 0) && anything has to be false. Therefore, it doesn't bother to evaluate the second operand, (B/A > 1). The evaluation has been short-circuited and the division by zero is avoided. Without the short-circuiting, there would have been a division-by-zero error. (This may seem like a technicality, and it is. But at times, it will make your programming life a little easier. To be even more technical: There are actually non-short-circuited versions of && and ||, which are written as & and |. Don't use them unless you have a particular reason to do so.)

The boolean operator "not" is a unary operator. In Java, it is indicated by ! and is written in front of its single operand. For example, if test is a boolean variable, then

test = !test;

will reverse the value of test, changing it from true to false, or from false to true. You could use

if ( !( (A == 0) && (B == 0) ) )

to test whether "it is not the case that both A equals 0 and B equals 0."


Conditional Operator

Any good programming language has some nifty little features that aren't really necessary but that let you feel cool when you use them. Java has the conditional operator. It's a ternary operator -- that is, it has three operands -- and it comes in two pieces, ? and :, that have to be used together. It takes the form

boolean-expression ? expression-1 : expression-2

The computer tests the value of boolean-expression. If the value is true, it evaluates expression-1; otherwise, it evaluates expression-2. For example:

N = (N % 2 == 0) ? (N/2) : (3*N+1);

has the same effect as the if statement:

                if (N % 2 == 0)
                   N = N/2;
                else
                   N = 3*N+1;

Assignment Operators

You are already familiar with the assignment statement, which uses the operator = to assign the value of an expression to a variable. In fact, = is really an operator, and an assignment can itself be used as an expression or as part of a more complex expression. The value of an assignment such as A=B is the same as the value that is assigned to A. So, if you want to assign the value of B to A and test at the same time whether that value is zero, you could say:

if ( (A=B) == 0 )

Usually, I would say, don't do things like that!

In general, the type of the expression on the right-hand side of an assignment statement must be the same as the type of the variable on the left-hand side. However, in some cases, the computer will automatically convert the type of value computed by the expression to match the type of the variable. Consider the list of numeric types: byte, short, int, long, float, double. A value of a type that occurs earlier in this list can be converted automatically to a value that occurs later. For example:

               int A = 17;
               double X = A;  // OK; A is converted to a double
               short B = A;   // illegal; no automatic conversion
                              //       from int to short

The idea is that conversion should only be done automatically when it can be done without changing the semantics of the value. Any int can be converted to a double with the same numeric value. However, there are int values that lie outside the legal range of shorts. There is simply no way to represent the int 100000 as a short, for example.

In some cases, you might want to force a conversion that wouldn't be done automatically. For this, you could use what is called a type cast. A type cast is indicated by putting a type name, in parentheses, in front of the value you want to convert. For example,

               int A = 17;
               short B = (short)A;  // OK; A is explicitly type cast
                                    //      to a value of type short

You can do type casts from any numeric type to any other numeric type. However, you should note that you might change the numeric value of a number by type casting it. For example, (short)100000 is 34464. (The 34464 is obtained by taking the 4-byte int 100000 and throwing away two of those bytes to obtain a short -- you've lost the real information that was in those two bytes.)

As another example of type casts, consider the problem of getting a random integer between 1 and 6. The function Math.random() gives a real number between 0.0 and 0.9999..., and so 6*Math.random() is between 0.0 and 5.999.... The type cast operator, (int), can be used to covert this to an integer: (int)(6*Math.random()). A real number is cast to an integer by discarding the fractional part. Thus, (int)(6*Math.random()) is one of the integers 0, 1, 2, 3, 4, and 5. To get a number between 1 and 6, we can add 1: "(int)(6*Math.random()) + 1".

You can also type cast between type char and the numeric types. The numeric value of a char is its Unicode code number. For example, (char)97 is 'a', and (int)'+' is 43.

Java has several variations on the assignment operator, which exist to save typing. For example, "A += B" is defined to be the same as "A = A + B". Operators -=, *=, /=, and %= are defined in a similar way.


Precedence Rules

If you use several operators in one expression, and if you don't use parentheses to explicitly indicate the order of evaluation, then you have to worry about the precedence rules that determine the order of evaluation. (Advice: don't confuse yourself or the reader of your program; use parentheses liberally.)

Here is a listing of the operators discussed in this section, listed in order from highest precedence (evaluated first) to lowest precedence (evaluated last):

      Unary operators:              ++, --, !, unary - and +, type cast
      Multiplication and division:  *,  /,  %
      Addition and subtraction:     +,  -
      Relational operators:         <,  >,  <=,  >=
      Equality and inequality:      ==,  !=
      Boolean and:                  &&
      Boolean or:                   ||
      Conditional operator:         ?:
      Assignment operators:         =  +=  -=  *=  /=  %=

Operators on the same line have the same precedence. When they occur together, unary operators and assignment operators are evaluated right-to-left, and the remaining operators are evaluated left-to-right. For example, A*B/C means (A*B)/C, while A=B=C means A=(B=C). (Can you see how the expression A=B=C might be useful, given that the value of "B=C" as an expression is the same as the value that is assigned to B?)


[ Next Section | Previous Section | Chapter Index | Main Index ]