CSE131: Data Types and Expressions

Copyright © 1996-2005, Kenneth J. Goldman

Everything in Java has a type that identifies the "kind of thing" it is.

Some primitive (built-in) data types in Java are:

Using primitive data is boring by itself. We need some means of combining data together. Java provides some primitive procedures, like addition, that can be used to create useful expressions.
EXPRESSION
anything you can say that has a value
We invoke many built-in procedures using operators like +. The syntax is infix, meaning that the operators go in between the operands, the values upon which they operate. For example, let's look at some integer expressions.

ExpressionValue
3 + 58
17 - (3 * 5)2
8 / -2-4
14 % 32 (modulus or remainder)
6 + 5 * 216

Precedence Rules

You can use parentheses to group expressions, but what happens if you don't? For example, how do we know that the last value in the table above is 16, and not 22? Is it ambiguous? No. Java uses the standard rules of algebra:
Evaluation is left to right, but
negation (-) takes precedence over
multiplication (*), division (/), and modulus (%), which take precedence over
addition and subtraction.

These are precedence rules, sometimes called order of operations.

Expression Trees

One way to think about an expression is with an expression tree, where the operands hang below their operator in the tree. The last calculation to be performed is at the top (root) of the tree. For example, consider the expression 6 + 5 * 2.

Now, consider (50 + 25 * 3) / (8 - 3). What's the last operation to be performed? Division. So we put the division operator at the top of the tree. The operands of the division are the values of the addition and subtraction expressions, and so on. Thus, we get the following expression tree.

Java evaluates left to right and bottom to top in this tree. Operands are always evaluated before the operation is performed.

One way to evaluate an expression tree is to work your way up, writing by each node the value that results from performing that operation on the child operands. Consider the tree above. For the multiplication node, we get 75. Then, for the addition node, we have operands 50 and 75, yeilding 125. For the subtraction node, we have 5. Finally, at the top we have 125 divided by 5, resulting in 25 as the final value of the expression.

Substitution Model

Using the substitution model, you replace expressions by simpler expressions or values until reaching a final value. For example,
            (50 + 25 * 3) / (8 - 3)
            (50 +   75  ) /    5
               125        /    5
                          5
Notice the similarity between this method and the evaluation of expression trees.

Notes: If you mix int and double values in an expression, the result is a double. Division by zero is considered an error. For example,

ExpressionValue
7.0 / 23.5
7 / 23 (value is truncated)
7 % 21
7 / (4 - 2 * 2)error (division by 0)

String Expressions

You don't add and subtract strings like numbers, but Java does provide an operator + for concatenation of strings.

ExpressionValue
"abc" + "def""abcdef"
"123" + "456""123456" (not 579)
"hello" + "world""helloworld"
"hello " + "world""hello world"

Java will convert other primitive values to strings in a concatenation.

ExpressionValue
"abc" + 123"abc123"
123 + "abc""123abc"
"123" + 456"123456"
123 + 456579

Boolean Expressions

Boolean expressions are those that evaluate to either true or false. For example, comparisons between numbers (equality, inequality, less than, etc.) are boolean expresions. Boolean expressions can be combined into larger boolean expressions using the connectives && and ||, which stand for AND and OR, respectively. The negation operator ! is used to negate an expression. Several examples follow.

ExpressionValue
3 == 3true (test for equality)
3 != 3false (test for inequality)
7 > 3
true
!(7 > 3)
false
(7 > 3) || (7 <= 3)
true
(7 >= 3) && (7 < 3)
false

Variables and Naming Abstraction

Like in algebra, we often want to use symbols instead of actual values in our expressions. Symbols (names for values) make expressions more general and easier to read. Each symbol, called a variable, can hold a value of a particular type. For example,
int n;
double y;
are declarations for an integer variable n and a double y. Think of a variable as a container with the "right shape" to hold a value of the declared type. We put a value into a variable using an assignment statement. For example,
n = 3;
y = 0.95 * 200;
n = 5;
would put the values 3 and 190 into the two variables, respectively. Then, the value in variable n would be overwritten with the value 5.

In general, a statement is a step to be executed in a computer program. In Java, both statements and declarations end with semicolons (;). Unless otherwise directed, Java executes statements in order, one after another.

In an assignment statement, the expression on the right is evaluated and then assigned to the variable on the left. The types must be compatible. For example,

int k;
k = "foo";   // ERROR -- type mismatch
For convenience, Java allows a declaration to be combined with an assignment on the same line, giving the declared variable an initial value. For example,
int i = 3;   // declares an integer variable i and initializes it to 3
Variables may take on new values as the result of subsequent assignment statements:
int j = 4;   // declares an integer variable j and initializes it to 4
j = 2;       // changes the  value of j to 2
Note: To prevent a variable ever being changed from its initial value, one may use Java's final modifier.
final double PI = 3.14159;
PI = 6;      // ERROR
We use uppercase for final variables (by convention). A final variable is often called a constant.

Using symbols in place of values and giving names to particular values are two forms of naming abstraction. The power of naming abstraction should not be underestimated. It saves us from retyping to the value all the time and makes programs easier to read.

People use names all the time to make our lives easier. Imagine if none of us had names, and we had to refer to people by description! ("The guy with brown hair and glasses.") Similarly, imagine if we all had one-letter names or unpronouncable names for objects. We choose names for their convenience and their descriptive power.

Just as in real life, it's important to choose meaningful names for variables and other things in a computer program. The computer doesn't care what names you pick, but it makes a huge difference to people who need to read your program. When you or someone else tries to modify your program later, having descriptive names that convey the purpose of the variable to the reader is extremely important.

Using Variables in Expressions

Suppose we want to compute the area of some circles with radii 3, 4, and 5.

Expression tree:


To accomplish this task, we could write the expressions:

double area3 = 3.14159 * 3 * 3;
double area4 = 3.14159 * 4 * 4;
double area5 = 3.14159 * 5 * 5;

But this is annoying! We're doing a lot of the same thing over and over. Specifically, we have to keep typing the value of pi over and over, and we must type the radius twice each time in order to square its value. Whenever you see a lot of repetetive work, it should clue you in that you need to use some kind of abstraction.

So far, we know about one means of abstraction, naming, so let's try to improve the situation using naming abstraction. We can use variable names in the formula instead of numbers, as follows.

final double PI = 3.14159;
int r = 3;
double area3 = PI * r * r;
r = 4;
double area5 = PI * r * r;
r = 5;
double area5 = PI * r * r;
In some ways this is better. We need only provide the value of pi once, and each radius is provided once. Furthermore, we can tell more easily what the formula means because descriptive variable names are used.

However, we still have to remember the formula and type it each time we want to compute the area of a circle.

There must be a better way...