Prelab 4
Prelab 4
Spring 2025
1 Binary Operations
In Java, when two values are combined with a binary operator (such as n + f), both operands are converted
to a common type before the operation is carried out:
• If either of the operands is of type double, the other one will be converted to a double.
• Otherwise, if either of the operands is of type float, the other one will be converted to a float.
• Otherwise, if either of the operands is of type long, the other one will be converted to a long.
• Otherwise, both operands will be converted to an int.
The solid arrows in Figure 1 denote conversions without information loss. The dotted arrows, on the
other hand, denote conversions that may lose precision.
1
Example conversion with precision loss: a large integer such as 123456789 has more digits than the
float type can represent. When the integer is converted to a float, the resulting value has the correct
magnitude but loses some precision.
1 int n = 123456789;
2 float f = n; // f is 1.23456792 E8
Example without precision loss: Java allows assigning an int to a double as shown below, and such a
conversion does not lead to any precision loss.
1 int n = 13;
2 double d = n; // implicit conversion ; no need for explicit casting
Note that the boolean type cannot be converted into another type (not even with casting)
2
• T and U are class types, and U is a superclass of T (i.e., class T extends class U )
• T and U are interface types, and U is a super-interface of T (i.e., interface T extends interface U )
• T is a class that implements interface U
The “widest” type is java.lang.Object.
Widening conversions do not require an explicit cast. For example, assume that the class Musician
extends (i.e., inherits from) the class Person, then the following conversions are possible:
1 Musician m = new Musician ();
2 Person p = m; // widening conversion (no explicit casting needed )
3 Musician m2 = ( Musician ) p; // narrowing conversion that requires explicit
casting
Note, however, that while the below conversion is accepted at compile time, it will fail at
runtime:
1 Person p = new Person ();
2 // Musician m = p; // compiler error : narrowing conversion , requires explicit
casting
3 Musician m = ( Musician ) p; // explicit casting removes the compilation error
but will fail at runtime ( ClassCastException )
In PersonDemo.java of Lab 4, you will loop over a list of Person objects. Some of these persons are
Musicians, and others are not. When you downcast the ith person to a Musician (Musician musician
= (Musician) person;), the casting will only be successful at runtime if the current person is actually
a musician. Particularly, the casting will not work for maya, who was instantiated as a Person, nor
for bahaa, who was instantiated as a Student. The casting will be successful only for the iterations
corresponding to dave and clara, who were instantiated as Musician objects.
From the compiler’s perspective, all these objects are of type Person, which is the declared type of
the list; that is, the compiler is unaware of the runtime types, which are the instantiation types (what
comes after the new keyword), because new is a runtime operation. Therefore, the compiler allows the
conversion in all the iterations, although not all of them will be successful at runtime. This is why in
PersonDemo.java, an instanceof check is used before downcasting so that the casting is safe and is
guaranteed to work at runtime. instanceof is a runtime check that verifies the runtime type of the
object.
3
autoboxing and unboxing, where it automatically converts between primitives and their wrappers when
necessary. For example, in the statement Integer num = 10;, Java autoboxes the primitive 10 into an
Integer object. Similarly, in int x = num;, Java unboxes num back into an int. These conversions
happen behind the scenes using the appropriate methods (the ones shown in the table in addition to other
more efficient methods such as Integer.valueOf()).
More on Integer.valueOf(): As mentioned above, you can assign a primitive value to its corresponding
wrapper type in Java. For example, you can write Integer x = 3; rather than Integer x = new Integer(3);
When we write, Integer x = 3; there is no conversion or casting happening (it is not possible to cast
a primitive to a reference type), but Java is calling Integer.valueOf(3) behind the scenes. This is called
(autoboxing) where the primitive type is auto-boxed into its wrapper type. But how does Integer.valueOf(3)
work? See the brain teaser at the end of this document.
4
6 No Casting between Unrelated Reference Types
Conversions or casting between unrelated reference types (with no inheritance relationship) is not possible.
For example, while we can convert the primitive int to a double or vice versa via casting, we cannot
directly convert nor cast the wrapper Integer to Double or vice versa. This is because Double is not a
super-class of Integer. If such a conversion is needed, we generally use special methods that do a similar
function (e.g., the valueOf method):
1 Integer x = 3;
2 Double d = Double . valueOf (x);
5
8 Brain Teaser
Consider the codes in Figure 2-a, and try to make sense of the outputs displayed. In particular, try to
understand why the output highlighted in red is not consistent with the output of the previous example
(although the scenario is exactly the same except for using the value 3 instead of 300!) Need a hint?
Check the documentation of Integer valueOf method (Figure 2-b).
dummy
Figure 2 - a Figure 2 - b