Skip to main content

Ch 2.5 Type Casting

When programming, there are times when you need to perform operations between variables or literals of different types. In such cases, you must first match the values to the same type before executing the operation. Converting the type of a variable or literal to another type like this is called Type Casting.

1. Two Kinds of Type Casting

KindDescriptionData Loss RiskSyntax
Implicit (Automatic) CastingPerformed automatically by the compilerNo data lossNo special syntax
Explicit (Manual) CastingSpecified directly by the developerPossible data loss(type) value

2. Implicit Casting (Automatic)

When converting from a smaller type to a larger type, it is safe so the compiler handles it automatically.

Direction of Implicit Casting (automatic conversion left to right)

byte → short → int → long → float → double

char ─┘
note

Why float (4 bytes) is to the right of long (8 bytes): float uses floating-point notation to express a wider range. (However, long may have higher precision.)

public class ImplicitCasting {
public static void main(String[] args) {
// byte -> int implicit conversion
byte b = 100;
int i = b; // Implicit casting (no data loss)
System.out.println("byte -> int: " + i); // 100

// int -> long implicit conversion
int intVal = 300;
long longVal = intVal;
System.out.println("int -> long: " + longVal); // 300

// int -> double implicit conversion
int num = 5;
double dbl = num;
System.out.println("int -> double: " + dbl); // 5.0

// long -> float implicit conversion
long bigNum = 1_000_000_000L;
float flt = bigNum;
System.out.println("long -> float: " + flt); // 1.0E9 (watch for precision loss!)

// char -> int implicit conversion
char ch = 'A';
int unicode = ch; // Unicode value of 'A' is 65
System.out.println("char -> int ('A'): " + unicode); // 65
}
}

Situations Where Implicit Casting Occurs

public class AutoCastingSituations {
public static void main(String[] args) {
// Situation 1: Arithmetic operations - matched to the larger type
int i = 10;
long l = 20L;
long result1 = i + l; // int is automatically converted to long
System.out.println("int + long = " + result1); // 30

// Situation 2: byte + byte -> int (byte arithmetic is converted to int!)
byte a = 10, b = 20;
// byte sum = a + b; // Error! byte + byte result is int
int sum = a + b; // OK
System.out.println("byte + byte = " + sum); // 30 (int)

// Situation 3: Automatic method argument conversion
printDouble(5); // int 5 automatically converted to double
printDouble(3L); // long 3 automatically converted to double

// Situation 4: Automatic conversion in ternary operator
int x = 10;
double d = (x > 5) ? 1 : 2.0; // int 1 automatically converted to double
System.out.println("Ternary result: " + d); // 1.0
}

static void printDouble(double d) {
System.out.println("double: " + d);
}
}

3. Explicit Casting (Manual)

When converting from a larger type to a smaller type, data loss can occur, so the developer must specify it explicitly.

// Syntax: (target_type) value
double d = 85.7;
int score = (int) d; // Decimal part truncated (NOT rounded!)
public class ExplicitCasting {
public static void main(String[] args) {
// double -> int: decimal part truncated
double d = 85.7;
int score = (int) d;
System.out.println("85.7 -> int: " + score); // 85 (truncated!)

// long -> int: data loss when out of range
long bigLong = 3_000_000_000L; // exceeds int range
int intFromLong = (int) bigLong;
System.out.println("3 billion -> int: " + intFromLong); // unexpected value!

// long -> int within range: safe
long safeLong = 100L;
int intFromSafe = (int) safeLong;
System.out.println("100L -> int: " + intFromSafe); // 100 (OK)

// float -> int
float f = 3.99f;
int truncated = (int) f;
System.out.println("3.99f -> int: " + truncated); // 3 (truncated!)

// int -> byte: data loss
int exceed = 300;
byte b = (byte) exceed;
System.out.println("300 -> byte: " + b); // 44 (data loss!)
}
}
warning

When explicitly casting, the decimal part is truncated (NOT rounded). Converting 3.99 to int gives 3, not 4. To round, use Math.round() or (int)(d + 0.5).

4. Integer-Float Conversion

public class IntDoubleConversion {
public static void main(String[] args) {
// int -> double: implicit (exact)
int intVal = 7;
double doubleVal = intVal;
System.out.println("7 -> double: " + doubleVal); // 7.0

// double -> int: decimal truncation
double pi = 3.14159;
int truncated = (int) pi;
System.out.println("3.14159 -> int: " + truncated); // 3

// What if you want to round?
int rounded = (int) Math.round(pi);
System.out.println("3.14159 rounded: " + rounded); // 3

double piLarge = 3.7;
int roundedLarge = (int) Math.round(piLarge);
System.out.println("3.7 rounded: " + roundedLarge); // 4

// Integer division pitfall!
int a = 5, b = 2;
int wrongResult = a / b; // Integer division -> 2 (decimal truncated)
double correctResult = (double) a / b; // Double division -> 2.5

System.out.println("5 / 2 (int): " + wrongResult); // 2
System.out.println("5 / 2 (double): " + correctResult); // 2.5
}
}

5. char-int Conversion and ASCII/Unicode Usage

Since char is internally stored as an integer (Unicode), it can be mutually converted with int.

public class CharIntConversion {
public static void main(String[] args) {
// char -> int: check Unicode value
char ch = 'A';
int unicode = ch; // Implicit conversion
System.out.println("'A' = " + unicode); // 65

// int -> char: create character from Unicode
int code = 65;
char fromCode = (char) code; // Explicit conversion required
System.out.println("65 = '" + fromCode + "'"); // 'A'

// Convert between uppercase and lowercase (difference is 32)
char upper = 'A';
char lower = (char)(upper + 32); // A(65) + 32 = a(97)
System.out.println("A -> a: " + lower);

// Reverse
char lowerB = 'b';
char upperB = (char)(lowerB - 32);
System.out.println("b -> B: " + upperB);

// char arithmetic
System.out.println('A' + 1); // 66 (int result)
System.out.println((char)('A' + 1)); // B (cast to char)

// Convert digit character to integer
char digitChar = '7';
int digit = digitChar - '0'; // '7'(55) - '0'(48) = 7
System.out.println("'7' - '0' = " + digit); // 7

// Key Unicode values
System.out.println("'0' = " + (int)'0'); // 48
System.out.println("'A' = " + (int)'A'); // 65
System.out.println("'a' = " + (int)'a'); // 97
}
}

6. String - Number Conversion

This is a very frequently used pattern in practice. User input is always a string, so converting it to a number is essential.

public class StringNumberConversion {
public static void main(String[] args) {
// === String -> Number ===
String strInt = "42";
String strDouble = "3.14";
String strLong = "9999999999";
String strBool = "true";

int parsedInt = Integer.parseInt(strInt);
double parsedDouble = Double.parseDouble(strDouble);
long parsedLong = Long.parseLong(strLong);
boolean parsedBool = Boolean.parseBoolean(strBool);

System.out.println("parseInt: " + parsedInt); // 42
System.out.println("parseDouble: " + parsedDouble); // 3.14
System.out.println("parseLong: " + parsedLong); // 9999999999
System.out.println("parseBoolean: " + parsedBool); // true

// === Number -> String ===
int num = 100;
double dbl = 3.14;

// Method 1: String.valueOf()
String fromInt1 = String.valueOf(num);
String fromDbl1 = String.valueOf(dbl);

// Method 2: Wrapper class .toString()
String fromInt2 = Integer.toString(num);
String fromDbl2 = Double.toString(dbl);

// Method 3: "" + number (simplest but not recommended)
String fromInt3 = "" + num;

System.out.println("valueOf: " + fromInt1); // "100"
System.out.println("toString: " + fromInt2); // "100"
System.out.println("concat: " + fromInt3); // "100"

// Handling invalid conversion
try {
int bad = Integer.parseInt("abc"); // Not a numeric string!
} catch (NumberFormatException e) {
System.out.println("Error: " + e.getMessage()); // For input string: "abc"
}
}
}
tip

When String to Number conversion fails, a NumberFormatException is thrown. When converting external input (user input, files, etc.), always add exception handling or validate that the string is in numeric format first.

7. Casting Warnings

Watch Out for Overflow

public class CastingWarnings {
public static void main(String[] args) {
// Overflow when force-casting long -> int
long largeNum = 3_000_000_000L; // 3 billion (exceeds int range)
int small = (int) largeNum;
System.out.println("3 billion -> int: " + small); // -1294967296 (data loss!)

// Check range before converting safely
if (largeNum <= Integer.MAX_VALUE && largeNum >= Integer.MIN_VALUE) {
int safe = (int) largeNum;
System.out.println("Safe conversion: " + safe);
} else {
System.out.println("Exceeds int range! Keeping as long.");
}

// float -> int precision loss
float floatVal = 16_777_217f; // 2^24 + 1 (cannot be represented exactly in float)
int fromFloat = (int) floatVal;
System.out.println("16777217f -> int: " + fromFloat); // 16777216 (error!)

// Overflow during calculation
int price = 1_000_000;
int quantity = 3_000;
int totalWrong = price * quantity; // Exceeds int range! Overflow
long totalRight = (long) price * quantity; // Cast to long first

System.out.println("Wrong calculation: " + totalWrong); // -1294967296
System.out.println("Right calculation: " + totalRight); // 3000000000
}
}

Watch Out for Precision Loss

public class PrecisionLoss {
public static void main(String[] args) {
// Possible precision loss when converting long -> float
long precise = 123_456_789_012_345L;
float imprecise = precise; // Implicit conversion but precision is lost!
long backToLong = (long) imprecise;

System.out.println("Original long: " + precise); // 123456789012345
System.out.println("As float: " + imprecise); // 1.2345679E14 (loss!)
System.out.println("Back to long: " + backToLong); // 123456791642112 (loss!)

// double is more precise but still has limits
double d = precise;
long backToLong2 = (long) d;
System.out.println("As double: " + d); // 1.2345678901234E14
System.out.println("Back to long: " + backToLong2); // More accurate but not perfect
}
}

8. instanceof Operator and Reference Type Casting

Type casting also occurs with reference types. Used for parent-child type conversion in inheritance relationships.

public class InstanceofExample {
static class Animal {
void speak() { System.out.println("..."); }
}
static class Dog extends Animal {
void speak() { System.out.println("Woof!"); }
void fetch() { System.out.println("Fetched the ball!"); }
}
static class Cat extends Animal {
void speak() { System.out.println("Meow!"); }
}

public static void main(String[] args) {
Animal animal = new Dog(); // Implicit casting (upcasting): Dog -> Animal

animal.speak(); // Woof! (polymorphism)
// animal.fetch(); // Error! Animal type has no fetch() method

// Check type with instanceof then explicit cast (downcasting)
if (animal instanceof Dog) {
Dog dog = (Dog) animal; // Explicit casting (downcasting)
dog.fetch(); // Woof!
}

// Java 16+ Pattern Matching - more concise approach
if (animal instanceof Dog d) {
d.fetch(); // Use directly without explicit cast
}

// ClassCastException example
Animal cat = new Cat();
try {
Dog wrongDog = (Dog) cat; // Cannot convert Cat to Dog!
} catch (ClassCastException e) {
System.out.println("ClassCastException: " + e.getMessage());
}
}
}
warning

Before explicitly casting a reference type, always check the type with instanceof. Incorrect casting causes a ClassCastException at runtime.

9. Practical Example: Why Casting Is Needed for Average Calculation

public class AverageCalculation {
public static void main(String[] args) {
int[] scores = {85, 92, 78, 96, 88, 74, 91, 83, 79, 95};

// === Wrong approach (integer division) ===
int total = 0;
for (int score : scores) {
total += score;
}
int wrongAverage = total / scores.length; // Integer division: decimal truncated
System.out.println("Wrong average: " + wrongAverage); // 86 (actual: 86.1)

// === Correct approach 1: cast before dividing ===
double correctAverage1 = (double) total / scores.length;
System.out.printf("Correct average 1: %.2f%n", correctAverage1); // 86.10

// === Correct approach 2: declare variable as double ===
double total2 = 0;
for (int score : scores) {
total2 += score; // int automatically converted to double
}
double correctAverage2 = total2 / scores.length;
System.out.printf("Correct average 2: %.2f%n", correctAverage2); // 86.10

// Max and min scores
int max = scores[0], min = scores[0];
for (int score : scores) {
if (score > max) max = score;
if (score < min) min = score;
}

System.out.println("Highest score: " + max);
System.out.println("Lowest score: " + min);
System.out.printf("Midpoint approximation: %.1f%n", (max + min) / 2.0);

// Grade calculation (using type casting)
System.out.println("\n=== Student Grades ===");
for (int i = 0; i < scores.length; i++) {
char grade;
if (scores[i] >= 90) grade = 'A';
else if (scores[i] >= 80) grade = 'B';
else if (scores[i] >= 70) grade = 'C';
else grade = 'D';

System.out.printf("Student %2d: %3d -> %c%n", i + 1, scores[i], grade);
}
}
}

Output:

Wrong average: 86
Correct average 1: 86.10
Correct average 2: 86.10
Highest score: 96
Lowest score: 74
Midpoint approximation: 85.0

=== Student Grades ===
Student 1: 85 -> B
Student 2: 92 -> A
Student 3: 78 -> C
Student 4: 96 -> A
Student 5: 88 -> B
...

10. Type Casting Summary Table

Conversion DirectionExampleAuto/ExplicitData Loss
byte -> intbyte b=1; int i=b;AutoNone
int -> longint i=1; long l=i;AutoNone
int -> doubleint i=5; double d=i;AutoNone (5.0)
char -> intchar c='A'; int i=c;AutoNone (65)
double -> int(int)3.14ExplicitDecimal truncated
long -> int(int)100LExplicitLoss if out of range
int -> char(char)65ExplicitNone ('A')
String -> intInteger.parseInt("42")MethodNone
int -> StringString.valueOf(42)MethodNone

Summary

  • Implicit casting: small -> large type (byte->short->int->long->float->double), safe
  • Explicit casting: (type) value, large -> small type, possible data loss
  • Float -> Integer: decimal part truncated(not rounded); use Math.round() to round
  • char ↔ int: mutually convertible via Unicode values
  • String ↔ Number: use methods like Integer.parseInt(), String.valueOf()
  • Average calculation: (double) total / count — casting is essential!