Type Conversion
Implicit Conversion: Widening
A widening conversion moves from a smaller (narrower) type to a larger (wider) type. No information is lost, so the compiler performs these conversions automatically — no explicit cast is needed.
The widening chain for numeric types:
byte → short → int → long → float → double
Additionally: char widens to int, and any integer type widens to float or double.
int i = 42;
long l = i; // OK — int fits in long
double d = i; // OK — int fits in double precisely (up to 2^53)
float f = 3.14f;
double d2 = f; // OK — float fits in double
The compiler silently inserts the widening conversion. The assignment long l = i compiles as though you wrote long l = (long) i.
Watch for Float Widening
long to float is a widening conversion — float has a larger range than long — but float has only ~7 significant digits of precision. Widening a large long to float preserves the magnitude but may lose low-order bits:
long big = 123456789123456789L;
float f = big; // compiles, but precision loss — f != big
Explicit Conversion: Narrowing
A narrowing conversion moves from a larger type to a smaller type. Information may be lost, so the compiler requires an explicit cast — the programmer must acknowledge the risk.
double d = 3.14159;
int i = (int) d; // truncates: i == 3
long l = 300L;
byte b = (byte) l; // overflow: 300 mod 256 = 44
float f = 5.7f;
int j = (int) f; // truncates: j == 5 (not rounded)
Narrowing an integer truncates the high-order bits. Narrowing a floating-point value truncates the fractional part — it does not round to nearest (use Math.round() for that).
Truncation vs Rounding
double positive = 3.7;
double negative = -3.7;
int a = (int) positive; // 3 (truncation toward zero)
int b = (int) negative; // -3 (truncation toward zero)
long c = Math.round(positive); // 4 (rounds to nearest)
long d = Math.round(negative); // -4
Integer Division Trap
When both operands are integers, Java performs integer division — the result is an integer, and any fractional part is discarded. This is a classic source of bugs:
double result = 5 / 2; // result is 2.0, not 2.5
Why? 5 and 2 are both int literals. Integer division yields 2. Only then is that 2 widened to 2.0 for assignment to double.
To get floating-point division, at least one operand must be a floating-point type:
double r1 = 5.0 / 2; // 2.5
double r2 = 5 / 2.0; // 2.5
double r3 = (double) 5 / 2; // 2.5
String Concatenation with +
The + operator is overloaded for strings. When one operand is a String, the other operand is converted to a String via toString(), and the two are concatenated:
String s = "Value: " + 42; // "Value: 42"
String t = "Result: " + 3.14; // "Result: 3.14"
String u = "Object: " + new Point(); // "Object: Point@1a2b3c" (or custom toString)
Concatenation is left-associative, which can produce surprising results:
String s = "Sum: " + 5 + 3; // "Sum: 53" — ("Sum: 5") then + 3
String t = "Sum: " + (5 + 3); // "Sum: 8" — parentheses force addition first
In the first line, "Sum: " + 5 produces "Sum: 5" (a string), then + 3 concatenates again. In the second line, (5 + 3) evaluates to 8 first, then the string concatenation runs.
Wrapper Class Conversions
Each primitive type has a corresponding wrapper class that provides utility methods for conversion between strings and primitives.
String to Primitive
int i = Integer.parseInt("42");
double d = Double.parseDouble("3.14");
boolean b = Boolean.parseBoolean("true");
long l = Long.parseLong("123456789");
parseInt and friends throw a NumberFormatException if the string is not a valid representation.
Primitive to String
String s1 = Integer.toString(42); // "42"
String s2 = Double.toString(3.14159); // "3.14159"
String s3 = String.valueOf(42); // "42" — works for any type
String s4 = "" + 42; // "42" — lazy concatenation trick
Autoboxing and Unboxing
Since Java 5, primitives are automatically boxed into their wrapper types and unboxed back:
Integer boxed = 42; // autoboxing — Integer.valueOf(42)
int unboxed = boxed; // auto-unboxing — boxed.intValue()
List<Integer> list = new ArrayList<>();
list.add(5); // autoboxing int to Integer
int value = list.get(0); // auto-unboxing Integer to int
Autoboxing creates hidden overhead — each boxing allocates an Integer object on the heap. In performance-sensitive loops, prefer primitives.
Conversion Hierarchy Summary
Widening (implicit, safe)
←←←←←←←←←←←←←←←←←←←
byte → short → int → long → float → double
↗
char
Narrowing (explicit cast, may lose data)
→→→→→→→→→→→→→→→→→→→