Now that we can check if an object is of a particular type, let’s see how to "transform" it into a different object reference type.
This process is called casting and we ask the compiler to do it by indicating the type we want in parenthesis before an object reference.
For example:
Light light1 = new BluetoothLight();
BluetoothLight btLight = (BluetoothLight) light1;
In this case, on the second line, we’re telling the Java compiler that we want to cast our light1
reference into a reference of type BluetoothLight
by doing (BluetoothLight) light1
.
Following from the example in the previous section, let’s assume we add a method that is specific to each type of light:
public class BluetoothLight implements Light {
//...
public void connectBluetooth(String bluetoothDevice, int pinCode) {
System.out.println("Connecting to Bluetooth device " + bluetoothDevice + " with pin: " + pinCode);
}
}
public class WiFiLight implements Light {
//...
public void connectWifi(String wifiNetworkName, String password) {
System.out.println("Connecting to WiFi: " + wifiNetworkName + " with password: " + password);
}
}
Now, let’s say we want to invoke the respective method depending on the type of Light
in our LightIdentifier
class. Both methods aren’t part of the shared Light
interface, so we’ll need to identify the type of object they are and then do the casting to be able to invoke them, as shown below:
public class LightIdentifier {
//...
public void connectTo(Light light) {
if (light instanceof BluetoothLight) {
BluetoothLight bluetoothLight = (BluetoothLight) light;
bluetoothLight.connectBluetooth("BTLIGHT", 1234);
} else if (light instanceof WiFiLight) {
WiFiLight wiFiLight = (WiFiLight) light;
wiFiLight.connectWifi("NetWorkName", "Som3Pa55word");
} else {
System.out.println("Don't know how to connect to that type of Light");
}
}
}
Let’s give this a go:
In the example code run the JavaTypeCastingApp
|
Light light1 = new BluetoothLight();
Light light2 = new WiFiLight();
LightIdentifier lightIdentifier = new LightIdentifier();
lightIdentifier.connectTo(light1);
lightIdentifier.connectTo(light2);
lightIdentifier.connectTo(null);
Output:
Connecting to Bluetooth device BTLIGHT with pin: 1234
Connecting to WiFi: NetWorkName with password: Som3Pa55word
Don't know how to connect to that type of Light
null
casting
You can cast null
to any type, this is a valid operation, for example, you could do:
Light a = null;
BluetoothLight b = (BluetoothLight) a;
Explicit vs Implicit Casting
Every time we assign an object reference of one type into an object reference of a different type, we’re actually doing a casting operation.
When we go from a subtype to a supertype in our hierarchy, the casting is assumed to be safe by the compiler. This means that the following 2 cases are equivalent:
Implicit Casting:
Light light1 = new BluetoothLight();
Explicit Casting:
Light light1 = (Light) new BluetoothLight();
However, when we go in the other direction in our type hierarchy, we can’t use implicit casting as seen previously and we need to tell the compiler what type we want to cast to.
Incorrect explicit casting
When doing explicit casting, the compiler will respect our decision to cast the given object reference into the type we’re telling it to. This means that any exceptions will be seen at runtime.
In the example code run the JavaTypeCastingExceptionApp
|
Light light1 = new BluetoothLight();
WiFiLight light2 = (WiFiLight) light1;
Output:
Exception in thread "main" java.lang.ClassCastException: io.jcoder.tutorials.ch06.casting.BluetoothLight cannot be cast to io.jcoder.tutorials.ch06.casting.WiFiLight
at io.jcoder.tutorials.ch06.casting.JavaTypeCastingExceptionApp.main(JavaTypeCastingExceptionApp.java:19)
In this case, we’re trying to convert light1
which is effectively a BluetoothLight
object to a WiFiLight
. In compilation time, we’ll get no errors in the code above, however, when you run the application you’ll get a ClassCastException
as Java can’t perform the cast from a BluetoothLight
into a WiFiLight
.
A note about primitives
Implicit and explicit casting can also be done on primitive types. Implicit casting can be used when you widen your data type, this is, going from a type that is smaller to a type that is larger, like from short
to long
.
In other cases, explicit casting is required, for example, going from int
to byte
.
Let’s look at some examples:
In the example code run the JavaPrimitiveCastingApp
|
byte a = 8;
int b = a;
System.out.println("The byte is equal to: " + a);
System.out.println("The int is equal to: " + b);
long c = 10;
short d = (short) c;
System.out.println("The long is equal to: " + c);
System.out.println("The short is equal to: " + d);
System.out.println("Overflow example: ");
int largerThanByte = 128;
byte overflow = (byte) largerThanByte;
System.out.println("The largerThanByte int is equal to: " + largerThanByte);
System.out.println("The casted byte is equal to: " + overflow);
Output:
The byte is equal to: 8
The int is equal to: 8
The long is equal to: 10
The short is equal to: 10
Overflow example:
The largerThanByte int is equal to: 128
The casted byte is equal to: -128
The first cast is an implicit one from byte
to int
, as we’re widening the data type we don’t get any compilation error and as expected both values equal 8.
The second cast is an explicit one from long
to short
, in this case we are narrowing the data type and as expected both variables have the same value.
Now, when doing explicit casting, you need to be aware of overflowing and underflowing. As covered in chapter 2, primitive types have a defined size / data range they can store and they can’t store any values outside that range.
In our last example, we’re assigning a value to a byte
variable. byte
variables have a range of -128
to 127
and the value we’re assigning to it is 128
. In this case, overflowing occurs and the result we get might not be what we expect (underflowing would occur if we assign a value lower than the minimum, like -129
).
There is no runtime exception in this case, the program will continue running. To get a runtime exception use static methods, like the one provided in Java’s Math.toIntExact(long input) static method.
|
This problem occurs in all types of applications. Possibly the most well known recent case of overflowing happened to YouTube when the Gangnam Style video became a hit. |