Java Operators

Types of operators

In general, Java has three types of operators: unary, binary and ternary. There are for, respectively, one, two or three operands.

Java operators are not necessarily evaluated from left-to-right order, altough this may be overriden by parenthesis ().

int cookies = 4;
double reward = 3 + 2 * --cookies;
print("You receive " + cookies + " cookies!")

The result is 9

operator precedence

Operator Symbols and examples
post-unary operators expression++, expression
pre-unary operators ++expression, expression
other unary operators -, !, ~, +, (type)
multiplication/division/modulus *, /, %
addition/subtraction +, -
shift operators «, », »>
relational operators <, >, <=, >=, instanceof
equal to/not equal to ==, !=
logical operators &, ^, |
short-circuit logical operators &&, ||
ternary operators boolean expression ? expression1:expression2
assignment operators =, +=, -=, *=, /=, %=, &=, ^=, |=, «=, »=, »>=

Watch out

Some operators need specific types. Be aware of tricks like the following

// in java true and 1 are not related at all. 
int pelican = !5; // DOES NOT COMPILE
boolean penguin = -true; // DOES NOT COMPILE
boolean peacock = !0; // DOES NOT COMPILE

Pay also attention for the difference between attendance++ and ++atendance.

What does this print?

int lion = 3;
int tiger = ++lion * 5 / lion--;
System.out.println("lion is " + lion);
System.out.println("tiger is " + tiger);

Hint: The result is lion == 3, tiger == 5.

Always verify parentheses syntax. You need to make sure they’re always valid and balanced.

long pigeon = 1 + ((3 * 5) / 3; // DOES NOT COMPILE
int blueJay = (9 + 2) + 3) / (2 * 4; // DOES NOT COMPILE
short robin = 3 + [(4 * 2) + 4]; // DOES NOT COMPILE

Brackets [] are not allowed in Java to be used in place of parentheses ().

Numeric Promotion Rules

  1. If two values have different data types, Java will automatically promote one of the values to the larger of the two data types.
  2. If one if the values is integral and the other is floating point, Java will automatically protmote the integral value to the floating-point type.
  3. Smaller data types such as byte, short, char are first promoted to int any time they’re used with a binary arithmetic operator, even if neither of the operands is int.
  4. After all promotion has occurred and the operands have the same data type, the resulting value will have the same data type as its promoted operands.
int x = 1;
long y = 33;
var z = x * y;
// z would be long

double x = 39.21;
float y = 2.1;
var z = x + y;
// this does not compile, because float are assumed to be double, unless postfixed with f

short x = 10;
short y = 3;
var z = x * y;
// z would be int

short w = 14;
float x = 13;
double y = 30;
var z = w * x / y;
// z would be double

Assigning Values

Pay attention to compilation errors from assignment operators. They’re often overlooked on the exam.

Assignment Operator

An assignment operator is a binary operator that modifies or assigns the variable on the left side of the operator, with the result of the value on the right side.

// this is the simplest assignment operator. 
int herd = 1;

Java automatically promotes from smaller to larger data types, but it will throw a compiler exception if you’re trying to convert from larget to smaller data types without casting.

Casting values

Casting is a unary operation where one data type is interpreted as another data type. It is optional and unnecessary when converting to a larger or widening data type, but it’s required whenconverting to a smaller or narrowing data type. Without casting, the compiler will generate an error when trying to put a larger data type inside a smaller one.

int hair = (short)20;
short tail = (short) (4+10); //applies to both
short tail = (short) 4+10; //only applies to 4!

long feathers = 10 (long) // DOES NOT COMPILE
float egg = 2.0 / 9; // DOES NOT COMPILE
int tadpole = (int)5 * 2L; // DOES NOT COMPILE
short frog = 3 - 2.0; // DOES NOT COMPILE

During ther exam, remember to kep track of parentheses and return types any time casting is involved.

Reviewing Primitive Assignment

// NONE OF THESE EXAMPLES COMPILE(!)
// trying to save a double in an Integer
int fish = 1.0;
// outside of the range of short
short bird = 1921222;
// trying to save a floating-point
int mammal = 9f; 
// this is an int because missing L;
long reptile = 1932012354896546321654687;

Applying Casting

// THIS EXAMPLES COMPILE
int trainer = (int) 1.0;
short ticketTaker = (short)1921222; // stored as 20678
int usher = (int)9f;
long manager = 192301238193810323L;
short mouse = 10;
short hamster = 3;
short capybara = mouse * capybara; // DOES NOT COMPILE

The last example does not compile, because short values are automatically promoted to int when applying any arithmetic operator, with the resulting value being of type int.

We can fix this by casting:

short mouse = 10;
short hamster = 3;
short capybara = (short)(mouse * hamster);

Watch out for the following examples.

short mouse = 10;
short hamster = 3;
// casting is only applies to 'mouse'
short capybara = (short) mouse * hamster; // DOES NOT COMPILE
// promoted to int, because short is being used at '+' operation
short gerbil = 1 + (short) (mouse * hamster); // DOES NOT COMPILE

Compund Assignment Operators

(+=, -=, *=, /=)

They’re just glorified forms of the simple assignment operator.

itn camel = 2, giraffe = 3;
// simple assignment operator
camel = camel * giraffe;
// compund assignment operator
camel *= giraffe;

The left side of the operator cannot be used to declare a new variable.

It can also save us from having to explicitly cast a value. The following example does not compile, because we’re trying to save a long in an int.

long goar = 10;
int sheep = 5;
sheep = sheep * goat; // DOES NOT COMPILE

This could be solved with an explicit cast to (int), but also with the compound operator. It will first cast sheep to a long, apply the multiplication and then cast the result to int.

long goat = 10;
int sheep = 5;
sheep *= goat; // COMPILES

Assignment Operator Return Value

The result of an assignment is an expression in and of itself, equal to the value of the assignment.

This is perfectly valid.

long wolf = 5;
long coyote = (wolf=3);
sout(wolf); // 3
sout(coyote); // 3

(wolf=3) does two things.

  1. It sets the value of the variable wolf to be 3.
  2. It returns a value of the assignment, which is also 3.

This is something to be expected at an exam

boolean healthy = false;
if(healthy = true) // (!) = is not the same as ==
	sout("Good!"); 
// this code snippet will print Good!

The previous if() is not comparing, it’s assigning healthy a value of true!.

Comparing Values

Equality Operators (== / !=)

In Java there’s a semantic difference between “two objects are the same” and “two objects are equivalent”. For numeric and boolean primitives, there’s no such distinction.

They’re used for the following scenarios:

  • Compare two numeric or char primitive types. If the numeric values are of different data types, the values are automatically promoted. f.e. 5 == 5.0 returns true since the left side is promoted to double.
  • Comparing two boolean values
  • Comparing two objects, including null and String values.

You cannot mix and match types.

boolean monkey = false != "Grape"; // DOES NOT COMPILE
boolean gorilla = true == 3; // DOES NOT COMPILE

Pay attention to mixed assignment and equality operators.

boolean bear = false;
boolean polar = (bear = true);
System.out.println(polar); // true

For object comparison, the equality operator is applies to the references to the objects, not the objects they point to. Two references are equal if and only if they point to the same object or both point to null.

File monday = new File("schedule.txt");
File tuesday = new File("schedule.txt");
File wednesday = tuesday;
System.out.println(monday == tuesday); // false
System.out.println(tuesday == wednesday); // true

Relational Operators (<, >=, a instanceof b)

a instanceof b - returns true if the reference that a points to, is an instance of a class, subclass or class that implements a particular interface, as named in b.

Numeric Comparison Operators

The operators (<, <=, >, >=) apply only to numeric values. If the two numeric operands are not of the same data type, the smaller one is promoted.

instanceof Operator

It is useful for determining whether an arbitrary object is a member of a particular class or interface at runtime (polymorphism).

All classes inherit from java.lang.Object. This means that any instance can be assigned to an Object reference.

Integer zooTime = Integer.valueOf(9);
Number num = zooTime;
Object obj = zooTime;

At the previous example, there’s only one object created in memory, but three different references to it. Integer inherits both Number and Object. This means you can call instanceof on any of these references and it would return true for the three of them.

Polymorphism often comes into play is when you create a method that takes a data type with many possible subclasses.

public void openZoo(Number time) {
	if(time instanceof Integer) 
		sout((Integer)time + "O'clock");
	else
		sout(time);
}

It’s considered a good coding practice to use the instanceof operator, prior to casting from one object to a narrower type.

Invalid instanceof

Be aware of trying to use instanceof with incompatible types during the exam.

public static void openZoo(Number time) {
	if(time instanceof String) // DOES NOT COMPILE
	// ...
}
null and the instanceof operator

Calling instanceof on the null literal or reference (almost) always returns false.

sout(null instanceof Object); // false 

Object none = null;
sout(none instanceof String); // false

sout(null instanceof null); // DOES NOT COMPILE

Logical Operators

Operators Description
& AND
| Inclusive OR
^ Exclusive XOR; only true if only one value is true

Logical operators may be applied to both numeric and boolean data types. When they’re applies to booleans, the’re referred to as logical operators. When they’re applied to numeric data types, they’re referred to as bitwise operators.

Short-Circuit Operators

Operators Description
&& Short-curcuit AND
|| Short-curcuit OR

If the final result can be determined by the left side of the expression, the right side will never be evaluated.

Avoiding a NullPointerException

The previous is usefull for checking null objects before performing an operation.

// if duck may be null, this wouldn't ever throw NPE. 
if(duck!=null && duck.getAge() < 5) {
}

Making Decisions with the Ternary Operator

The ternary operator ? : is notable in that it’s the only operator that takes three operands.

int food = (owl < 2) ? 3 : 4;

The first operand must be a boolean. The second and third operands can be any expression that returns a value.

There is no requirement for the second and third expressions to be the same data types.

int stripes = 7; 
System.out.print((stripes > 5) ? 21 : "Zebra"); // COMPILES
int animal = (stripes < 9) ? 3 : "Horse"; // DOES NOT COMPILE

Summary

Watch out, there will likely be numerous questions on the exam that appear to test one thing, such as StringBuilder or exception handling, when in fact the answer is related to the misuse of a particular operator that causes the application to fail to compile.

When you see an operator involving numbers on the exam, always check that the appropiate datatypes are used and that they match each other where applicable.

Exam Essentials

Be able to write code that uses Java operators. This chapter covered a wide variety of operator symbols.

Be able to recognize which operators are associated with which data types. Some operators may be applied only to numeric primitives, some only to boolean values, and some only to objects. It is important that you notice when an operator and operand(s) are mismatched, as this issue is likely to come up in a couple of exam questions.

Understand when casting is required or numeric promotion occurs. Whenever you mix operands of two different data types, the compiler needs to decide how to handle the resulting data type. When you’re converting from a smaller to a larger data type, numeric promotion is automatically applied. When you’re converting from a larger to a smaller data type, casting is required.

Understand Java operator precedence. Most Java operators you’ll work with are binary, but the number of expressions is often greater than two. Therefore, you must understand the order in which Java will evaluate each operator symbol.

Be able to write code that uses parentheses to override operator precedence. You can use parentheses in your code to manually change the order of precedence.