Java Type Casting

Java has two groups of data types: primitive and reference types. Primitive data types are built-in and include eight types, such as int (integer number), float and double (fractional numbers), and boolean (true or false values).

Reference types, on the other hand, are used to represent objects, and they are created using the keyword new. When a reference type is created, memory is allocated for the object, and it is called instantiation.

Floating-point types, such as float and double, represent numbers that contain an integer part, a fractional part, and their separator, commonly used in fields such as science, statistics, and engineering.

Suppose you need to assign a value of one type to a variable of another type. To do that, your program needs to cast the source type to the target type. Java provides two kinds of casting for primitive types: implicit and explicit. The first one is performed automatically by the Java compiler when it is possible, while the second one can only be done by a programmer.

In Java, a compiler is a program that takes Java source code and converts it into bytecode, which is an intermediate representation of the program. This bytecode is stored in a .class file and can be executed by the Java Virtual Machine (JVM). The compiler is responsible for catching and reporting any compile-time errors in the source code, such as syntax errors or mismatched method declarations. Additionally, modern IDEs often include static code analyzers that work with the compiler to identify potential errors and provide suggestions for improving the code before the compilation process even begins.

Implicit casting

In Java, implicit casting, also known as automatic casting, is a method or process where the compiler automatically converts a value of one type to another type without requiring any explicit instruction from the programmer. The compiler automatically performs implicit casting when the target type is wider than the source type. The picture below illustrates the direction of this casting. Any value of a given type can be assigned to the one on the right implicitly or below in the case of char.

implicit casting
The direction of implicit primitive type castings

Normally, there is no loss of information when the target type is wider than the source type, for example, when we cast int to long. But it is not possible to automatically cast in the backward order (e.g. from long to int or from double to float).

Note that there is no boolean type on the picture above because it is impossible to cast this type to any other and vice versa.

Here are several examples of implicit castings:

  • from int to long:
int num = 100;
long bigNum = num; // 100L
  • from long to double:
long bigNum = 100_000_000L;
double bigFraction = bigNum; // 100000000.0
  • from short to int:
short shortNum = 100;
int num = shortNum; // 100
  • from char to int:
char ch = '?';
int code = ch; // 63

In some cases, implicit type casting may result in some information loss. When we convert an int to float, or a long to float or double, we may lose some less significant bits of the value, which will result in a loss of precision. However, the result of this conversion will be a correctly rounded version of the integer value, which will be in the overall range of the target type. To understand this phenomenon, check out this example:

long bigLong =  1_200_000_002L;
float bigFloat = bigLong; // 1.2E9 (= 1_200_000_000)

When we convert a char to an int in Java, we actually get the ASCII value for that given character. The ASCII value is an integer representation of English alphabet letters (both uppercase and lowercase), digits, and other symbols. Here you can find some of the standard symbols in ASCII.

char character = 'a';
char upperCase = 'A';

int ascii1 = character; // this is 97
int ascii2 = upperCase; // this is 65

Strictly speaking, Java uses Unicode Character Representations (UTF-16), which is a superset of ASCII and includes a far larger set of symbols. However, the numbers 0–127 have the same values in both ASCII and Unicode.

As you can see, implicit casting works absolutely transparently.

Explicit casting

The considered implicit type casting does not work when the target type is narrower than the source type. But programmers can apply explicit type casting to a source type to get another type they want. It may lose information about the overall magnitude of a numeric value and may also lose precision.

To perform explicit type casting, a programmer must write the target type in parentheses before the source.

(targetType) source

Any possible typecasting not presented in the picture above needs such an approach, for example double to int, or long to char.

Examples:

double d = 2.00003;

// it loses the fractional part
long l =  (long) d; // 2

// requires explicit casting because long is wider than int
int i = (int) l; // 2 

// requires explicit casting because the result is long (indicated by L)
int val = (int) (3 + 2L); // 5

// casting from a long literal to char
char ch = (char) 55L; // '7'

However, the explicit casting may truncate the value because long and double can store a much larger number than int.

long bigNum = 100_000_000_000_000L;
int n = (int) bigNum; // 276447232

Oops! The value has been truncated. This problem is known as type overflow. The same problem may occur when casting int to short or byte. Let's see what happens exactly.

As you might know, long is a 64-bit number in Java programming language, whereas int is 32-bit. When converting long to int the program just takes the last 32 bits to represent the new number. If the long contains a number less than or equal to Integer.MAX_VALUE you can convert it by casting without losing information. Otherwise, the result will be quite meaningless, although determined. That is why you shouldn't perform casting from a larger type to a smaller type unless you are absolutely sure that it is necessary and that truncation will not interfere with your program.

You can use explicit casting even when the compiler will use implicit cast by default.

int num = 10;
long bigNum = (long) num; // redundant casting

But this is redundant and should not be used to avoid unnecessary constructs in your code.

Note, that despite the power of explicit casting, it is still impossible to cast something to and from the boolean data type.

Conclusion

If you want to type cast a narrower type to a wider type, you do not need to write anything, the Java compiler will do it automatically for you. But if you want the opposite, specify the required type in parentheses following the assignment operator. Keep in mind, the boolean type cannot be cast to another type and vice versa.

Create a free account to access the full topic

“It has all the necessary theory, lots of practice, and projects of different levels. I haven't skipped any of the 3000+ coding exercises.”
Andrei Maftei
Hyperskill Graduate