Type Casting and Type Conversion in Java
Widening happens by itself, narrowing needs a cast and can lose data, and casting a reference only changes how the compiler sees an object.
-
Java Basics
- Introduction to Java
- Setting Up Java and Writing Your First Program
- Variables, Data Types and Literals in Java
- Type Casting and Type Conversion in Java
- Operators and Expressions in Java
- Input and Output in Java
- Comments, Keywords and Naming Conventions in Java
- Control Flow in Java: if, else and switch
- Loops in Java: for, while and do-while
- Methods
- Arrays and Strings
-
OOP
- Classes and Objects in Java
- Constructors in Java
- The this Keyword in Java
- The static Keyword in Java
- Encapsulation in Java
- Access Modifiers in Java
- Inheritance in Java
- Method Overriding and super in Java
- Polymorphism in Java
- Abstraction, Abstract Classes and Interfaces in Java
- Composition, Aggregation and Association in Java
- The Object Lifecycle in Java
- Core Java
- Exception Handling
-
Collections
- The Java Collections Framework
- List in Java: ArrayList, LinkedList, Vector and Stack
- Set in Java: HashSet, LinkedHashSet and TreeSet
- Map in Java: HashMap, LinkedHashMap and TreeMap
- How HashMap Works Internally in Java
- Queue and Deque in Java: ArrayDeque and PriorityQueue
- Iterators in Java
- Comparable and Comparator in Java
- Collections Utilities and Choosing the Right Collection
- Generics
- Java 8+
- Stream API
- Date and Time
- File and I/O
-
Multithreading
- Threads in Java: Processes, Runnable and Thread
- Thread Lifecycle in Java
- Synchronization in Java: synchronized and volatile
- Locks and Atomic Classes in Java
- Race Conditions and Deadlocks in Java
- The Executor Framework and Thread Pools in Java
- Future and CompletableFuture in Java
- Concurrent Collections in Java
- The Java Memory Model
- JVM and Memory
- Advanced Java
- Networking
- JDBC
- Testing
What conversion means
Java converts a value from one type to another in two situations: when the compiler can prove that nothing is lost, and when you explicitly take responsibility with a cast.
Widening conversion
A smaller type fits inside a larger one, so the conversion is automatic.
byte -> short -> int -> long -> float -> double
char -> intint units = 250;
long total = units; // widening, no cast needed
double rate = total; // widening againWidening fromlongtofloatis automatic but not lossless. A 64 bit integer can carry more significant digits than a 32 bit float can represent, so precision may still be lost.
Narrowing conversion
Going the other way may not fit, so the compiler demands an explicit cast.
double price = 249.99;
int rupees = (int) price; // 249, the fraction is discarded not rounded
int large = 300;
byte small = (byte) large; // 44, the extra bits are simply droppedNarrowing an integer type keeps only the low order bits. The result is well defined, but it is rarely what was intended.
Numeric promotion in expressions
Before arithmetic, operands are promoted. Anything smaller than int becomes int, and if either operand is larger, both are promoted to the larger type.
byte a = 10, b = 20;
// byte sum = a + b; // compile error: a + b is an int
byte sum = (byte) (a + b); // explicit and correct
System.out.println(1 / 2); // 0 both operands are int
System.out.println(1 / 2.0); // 0.5 the int is promoted to doubleOverflow
int max = Integer.MAX_VALUE;
System.out.println(max + 1); // a large negative number, silently
// Fail loudly instead
System.out.println(Math.addExact(max, 1)); // throws ArithmeticExceptionInteger overflow does not throw by default; it wraps around. The Math.addExact family exists for arithmetic where wrapping would be a serious bug.
char and int
char grade = 66;
System.out.println(grade); // B
System.out.println((int) grade); // 66
System.out.println(grade + 1); // 67, because char is promoted to intCasting references
A reference cast never changes the object. It changes only the type through which the compiler lets you view it.
Object value = "a text value"; // upcast, always safe, implicit
String text = (String) value; // downcast, checked at runtime
Integer wrong = (Integer) value; // compiles, throws ClassCastExceptionThe safe form combines the test and the cast in one step, using pattern matching for instanceof from Java 16:
if (value instanceof String text) {
System.out.println(text.length());
}Strings are not converted by casting
String input = "42";
// int n = (int) input; // will not compile
int n = Integer.parseInt(input); // parsing, not casting
String back = String.valueOf(n); // or Integer.toString(n)Common mistakes
- Expecting
(int)to round. It truncates towards zero. UseMath.roundto round. - Writing
long ms = 24 * 60 * 60 * 1000 * 1000;and overflowing before the assignment. Make the first operand along. - Downcasting without checking the actual type first.
- Believing a cast can convert unrelated types. It cannot; only a real subtype relationship works.
Best practices
- Let widening happen naturally, and treat every cast as a claim you must justify.
- Use pattern matching for
instanceofinstead of a separate test and cast. - Use
Math.toIntExactand theExactmethods where silent overflow would be dangerous.
Practice
- What does
System.out.println((int) -2.7);print, and why is it not -3? - Fix
long nanos = 1000 * 1000 * 1000 * 10;so it holds the intended value. - Explain why
Object o = new int[3];compiles. - Write a method that safely converts an
Objectto aString, returning a default when the type does not match.
Conclusion
Widening is automatic, narrowing is a deliberate cast that can lose data, and reference casting only reinterprets an existing object. Treat every cast you write as a promise the runtime will check.