Home/Learn/Java A–Z/Type Casting

Type Casting

Beginner
OOP & Advanced Classes

Understand implicit widening, explicit narrowing casts for primitives, and safe object upcasting and downcasting with instanceof guards.

Overview

Type casting in Java comes in two flavours: primitive casting (between numeric types) and reference casting (between objects in a hierarchy). Widening (small → large type) is implicit and safe. Narrowing (large → small) requires an explicit cast and can lose data. For objects, upcasting (subtype → supertype) is always implicit; downcasting (supertype → subtype) requires an explicit cast and a runtime instanceof check to avoid ClassCastException.

Primitive Widening & Narrowing

Widening conversions are automatic: byte → short → int → long → float → double. No data loss for integral types widening to larger integral types, but long → float or long → double can lose precision because floating-point cannot represent all 64-bit integers exactly.

Narrowing conversions require an explicit cast: (int) 3.9 truncates to 3 — it does not round. Casting to a smaller type truncates high-order bits silently: (byte) 300 = 44 (300 % 256). This is a common source of overflow bugs.

PrimitiveCasting.java
public class PrimitiveCasting {
    public static void main(String[] args) {
        // Widening — implicit, safe
        int   i = 100;
        long  l = i;         // int → long: implicit
        float f = l;         // long → float: implicit (may lose precision!)
        double d = f;        // float → double: implicit
        System.out.println(d); // 100.0

        // Precision loss: long → float
        long big = 123_456_789_123L;
        float approx = big;          // implicit widening
        System.out.println(big);     // 123456789123
        System.out.println(approx);  // 1.23456794E11 — precision lost!

        // Narrowing — explicit cast required
        double pi = 3.14159;
        int truncated = (int) pi;     // truncates, does NOT round
        System.out.println(truncated); // 3

        // Truncation wraps on overflow
        int big2   = 300;
        byte small = (byte) big2;     // 300 % 256 = 44
        System.out.println(small);    // 44

        // Numeric promotion in expressions
        byte a = 10, b = 20;
        // byte result = a + b;  // compile error! a+b is promoted to int
        byte result = (byte)(a + b);  // explicit cast back
        System.out.println(result);   // 30

        // char ↔ int casting
        char c = 'A';
        int ascii = c;               // widening char → int
        System.out.println(ascii);   // 65
        char back = (char)(ascii + 1); // narrowing int → char
        System.out.println(back);    // B
    }
}

Reference Casting & ClassCastException

Upcasting assigns a subtype reference to a supertype variable — always safe, always implicit. The object is still the original subtype at runtime; you just see a narrower view of its API.

Downcasting recovers the original subtype reference. It fails at runtime with ClassCastException if the actual object is not the expected subtype. Always use instanceof before downcasting — or better, pattern-matching instanceof (Java 16+) which combines check and cast in one expression.

ReferenceCasting.java
public class ReferenceCasting {

    static class Animal { public String sound() { return "..."; } }
    static class Dog extends Animal {
        @Override public String sound() { return "Woof"; }
        public String fetch() { return "Fetching!"; }
    }
    static class Cat extends Animal {
        @Override public String sound() { return "Meow"; }
        public String purr()  { return "Purring..."; }
    }

    public static void main(String[] args) {
        // Upcasting — implicit, safe
        Animal a1 = new Dog();   // Dog IS-A Animal
        Animal a2 = new Cat();

        System.out.println(a1.sound()); // Woof — runtime dispatch
        // a1.fetch(); // compile error — Animal doesn't have fetch()

        // Old-style downcasting — manual check + cast
        if (a1 instanceof Dog) {
            Dog dog = (Dog) a1;        // safe — instanceof confirmed
            System.out.println(dog.fetch()); // Fetching!
        }

        // Pattern-matching instanceof (Java 16+) — check + bind in one line
        if (a2 instanceof Cat cat) {
            System.out.println(cat.purr()); // Purring...
        }

        // ClassCastException without guard
        try {
            Dog badDog = (Dog) a2;  // a2 is actually a Cat
        } catch (ClassCastException e) {
            System.out.println("ClassCastException: " + e.getMessage());
        }

        // Array of mixed animals — polymorphic processing
        Animal[] animals = { new Dog(), new Cat(), new Dog() };
        for (Animal a : animals) {
            String extra = switch (a) {           // pattern switch (Java 21)
                case Dog dog -> dog.fetch();
                case Cat cat -> cat.purr();
                default      -> "unknown";
            };
            System.out.println(a.sound() + " | " + extra);
        }
    }
}

Casting Pitfalls & Rules Summary

Key rules to remember:

1. Primitive widening: byte→short→int→long→float→double (left-to-right is automatic) 2. Narrowing truncates — it never rounds, and it wraps on overflow 3. long→float and long→double can lose precision even though it's a widening conversion 4. In arithmetic expressions, operands are promoted to at least int — even byte+byte is int 5. char is an unsigned 16-bit int — widening to int gives the Unicode code point 6. Object downcast without instanceof → ClassCastException at runtime 7. Arrays are covariant: String[] IS-A Object[], but assigning an Integer into a String[] throws ArrayStoreException

CastingPitfalls.java
public class CastingPitfalls {
    public static void main(String[] args) {
        // Pitfall 1: long → float precision loss (widening but lossy)
        long precise = 9_999_999_999_999_999L;
        float lossy  = precise;              // widening — but float can't represent this!
        System.out.println(precise);         // 9999999999999999
        System.out.println((long) lossy);    // 10000000000000000 — wrong!

        // Pitfall 2: byte arithmetic promotes to int
        byte x = 127;
        x = (byte)(x + 1);                  // must cast back to byte
        System.out.println(x);              // -128 — overflow wraps

        // Pitfall 3: division stays integer if both operands are int
        int a = 5, b = 2;
        System.out.println(a / b);           // 2 — not 2.5!
        System.out.println((double) a / b);  // 2.5 — cast ONE operand

        // Pitfall 4: Array covariance → ArrayStoreException
        Object[] objs = new String[3];       // upcasting array reference
        objs[0] = "hello";                   // fine
        try {
            objs[1] = 42;                    // runtime: ArrayStoreException
        } catch (ArrayStoreException e) {
            System.out.println("ArrayStoreException: " + e.getMessage());
        }

        // Pitfall 5: Comparing mixed numeric wrappers
        Long lVal = 100L;
        Integer iVal = 100;
        System.out.println(lVal.equals(iVal));   // false — different types!
        System.out.println(lVal.longValue() == iVal.intValue()); // true — compare primitives
    }
}

Key Points to Remember

  • Widening (byte→int→long→double) is implicit; narrowing requires an explicit cast and can lose data
  • Narrowing truncates toward zero — (int) 3.9 = 3; it never rounds
  • Byte arithmetic is promoted to int — (byte)(a + b) needs the explicit cast back
  • Upcasting is always safe; downcasting needs instanceof guard or throws ClassCastException
  • Pattern-matching instanceof (Java 16+) is the modern way: if (a instanceof Dog dog)
  • Array covariance (String[] IS-A Object[]) allows upcast but ArrayStoreException on wrong insert

Practice Type Casting in the Playground

Run and modify code directly in your browser - no setup needed.

Interview Questions

Sign in to ask Aria
1

What is the difference between widening and narrowing type conversion?

EasyTCS
2

What does (int) 3.99 evaluate to in Java?

EasyWipro
3

What is ClassCastException and how do you prevent it?

EasyAmazon
4

Why does long → float sometimes lose precision even though it's a widening cast?

MediumGoogle
5

What is array covariance and what problem can it cause?

MediumOracle

Ask Aria about Type Casting

Your personal AI tutor — ask anything about this concept