Chapter 6 - Java for Beginners Course

Type Casting

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.
Code in GitHub

Get the code for this tutorial using the links below.

Project Repo
Download code for this step
Main class for this step
Dependencies

This is a list of recommended tutorials or courses that might be useful before starting this one.

Contents
Welcome to the Course!
Course Introduction
Chapter 1 - Building Blocks
Quick introduction to Java Variables Classes And Objects Class Example - Defining a class Object Examples - Creating instances Java Application Example - Running our first app Accessing class members - The dot operator Packages - Organizing the code
Chapter 2 - Primitives and Operators
Primitives Arithmetic Operators Assignment Operator Unary Operators Equality and Relational Operators Conditional Operators
Chapter 3 - Statements and Control Flow
Expressions Statements If-Then Statement If-Then-Else Statement More If Statements Switch Statement While and Do-While Statements For Statement Branching Statements Exception Handling
Chapter 4 - Code Example
Example Project - A Simple Vending Machine Adding money Delivering Items Giving Change
Chapter 5 - Classes and Interfaces
Introduction Access Level Modifiers Class Declaration - Class, Methods and Fields Class Declaration - Constructors Inheritance Basics Inheritance - Constructors Inheritance - Methods and Fields Polymorphism Abstract Classes and Methods Interfaces Static Class Members Class Composition Final Classes and Class Members Generic Classes
Chapter 6 - Base Object Behaviors
Introduction Type Comparison Type Casting Object Equality - The Contract Object Equality - Common Pitfalls Object String Representation Garbage Collection Object Comparison Primitive Wrappers and Autoboxing
Chapter 7 - Data Structures
Introduction Arrays - Declaration and Creation Arrays - Basic Operations Core Collection Interfaces List and ArrayList - Basic Operations ArrayList Internals Introduction to Hash Tables Map and HashMap - Basic Operations Set and HashSet - Basic Operations
Chapter 8 - Anonymous classes and lambdas
Introduction Filtering a List Anonymous Classes Lambdas Built-in Functional Interfaces
Chapter 9 - Streams
Introduction Creating Streams Intermediate Operations Terminal Operations