Section 2.5
Details of Expressions
THIS SECTION TAKES A CLOSER LOOK at
expressions. Recall that an expression is a piece of program
code that represents or computes a value. An expression can be
a literal, a variable, a function call, or several of these things
combined with operators such as + and >.
The value of an expression can be assigned to a variable, used as the
output value in an output routine, or combined with other values into
a more complicated expression. (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.) Expressions are an essential part of
programming. So far, these notes have dealt only informally
with expressions. This section tells you the more-or-less complete
story.
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
TextIO class and the mathematical functions
from the Math class.
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 first 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 one!
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: For example, when the computer evaluates 1.0/N, it
first 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,
while 34577 % 100 is 77,
and 50 % 8 is 2. A common use of % is to test whether a given integer
is even or odd. N is even if N % 2 is zero, and it is odd if N % 2 is 1.
More generally, you can check whether an integer N is evenly divisible by an integer M
by checking whether N % M is zero.
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. You might perform the operation of adding 1 to a variable
with assignment statements such as:
counter = counter + 1;
goalsScored = goalsScored + 1;
The effect of the assignment statement x = x + 1 is to take the old value
of the variable x, compute the result of adding 1 to that value, and store
the answer as the new value of x.
The same operation 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
counter++;
goalsScored++;
Similarly, you could write x-- (or --x) to subtract 1
from x.
That is, x-- performs the same computation as x = x - 1.
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 numerical types and also
on variables of type char.
Usually, the operators ++ or --, are used in 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;
TextIO.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, but it will change the value of y to 6
since the value assigned to y is the old value of x.
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. I will follow this advice in all the
examples in these notes.
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 values 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. In the next chapter, you will see how to use them in
loop and branch statements. But you can also assign boolean-valued expressions
to boolean variables, just as you can assign numeric values to numeric variables.
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;
sameSign = ((x > 0) == (y > 0));
One thing that you cannot do with the
relational operators <, >, <=, and <=
is to use them to compare values of type String.
You can legally use == and != to compare
Strings, but because of peculiarities in the way
objects behave, they might not give the results you want. (The == operator
checks whether two objects are stored in the same memory location,
rather than whether they contain the same value. Occasionally,
for some objects, you do want to make such a check -- but rarely for strings.
I'll get back to this in a later chapter.) Instead, you should
use the subroutines equals(), equalsIgnoreCase(), and compareTo(),
which were described in Section 3, to compare two Strings.
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 also a boolean value. The result 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, |.) The expression
"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
(x != 0) && (y/x > 1)
Suppose that the value of x is in fact zero. In that case, the
division y/x is illegal, since division by zero is not allowed.
However, the computer will never perform the division, since
when the computer evaluates (x != 0), it finds that the result is
false, and so it knows that ((x != 0) &&
anything) has to be false. Therefore,
it doesn't bother to evaluate the second operand, (y/x > 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.
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:
next = (N % 2 == 0) ? (N/2) : (3*N+1);
will assign the value N/2 to next if N is even (that is, if
N % 2 == 0 is true), and it will assign the value (3*N+1)
to next if N is odd.
Assignment Operators and Type-Casts
You are already familiar with the assignment statement, which uses
the symbol "=" to assign the value of an expression to a variable.
In fact, = is really an operator in the sense that 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
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;
double X;
short B;
A = 17;
X = A; // OK; A is converted to a double
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, since the largest value of
type short is 32767.
In some cases, you might want to force a conversion that
wouldn't be done automatically. For this, you can 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;
short B;
A = 17;
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 convert 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 the 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". Every operator in Java
that applies to two operands gives rise to a similar assignment operator.
For example:
x -= y; // same as: x = x - y;
x *= y; // same as: x = x * y;
x /= y; // same as: x = x / y;
x %= y; // same as: x = x % y; (for integers x and y)
q &&= p; // same as: q = q && p; (for booleans q and p)
The combined assignment operator += even works with strings.
You will recall from Section 3 that when the + operator
is used with a string as the first operand, it represents concatenation.
Since str += x is equivalent to str = str + x,
when += is used with a string on the left-hand side, it
appends the value on the right-hand side onto the string. For example, if str has the
value "tire", then the statement str += 'd'; changes the
value of str to "tired".
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?)
End of Chapter 2