Every expression has a type, and the place where it is used may need a different one. A conversion changes a value from one type to another; a context is a kind of place in the program, and decides which conversions may happen there without a cast (JLS chapter 5).
Kinds of conversion
| Conversion | JLS | Example |
|---|---|---|
| Identity | 5.1.1 | int to int |
| Widening primitive | 5.1.2 | int to long |
| Narrowing primitive | 5.1.3 | long to int |
| Widening and narrowing primitive | 5.1.4 | byte to char: widened to int, then narrowed |
| Widening reference | 5.1.5 | String to Object |
| Narrowing reference | 5.1.6 | Object to String, checked at run time |
| Boxing | 5.1.7 | int to Integer |
| Unboxing | 5.1.8 | Integer to int |
| Unchecked | 5.1.9 | Raw List to List<String>, with a warning |
| Capture | 5.1.10 | List<?> to List<CAP#1> |
| String | 5.1.11 | Any value to String, in "n=" + n |
Anything else is forbidden (5.1.12): for example there is no conversion between
boolean and any other primitive type, and none between unrelated final
classes such as String and Integer, even with a cast.
Widening primitive conversions
There are 19 widening primitive conversions. They never throw and never lose the magnitude of a value:
| From | To |
|---|---|
byte | short, int, long, float, double |
short | int, long, float, double |
char | int, long, float, double |
int | long, float, double |
long | float, double |
float | double |
Three of them may lose precision, rounding to the nearest value: int to
float, long to float and long to
double. So (float) 16_777_217 is 16,777,216. Widening a
signed integer extends the sign; widening a char fills with zeros.
Narrowing primitive conversions
There are 22 narrowing primitive conversions, and they also never throw:
| From | To |
|---|---|
short | byte, char |
char | byte, short |
int | byte, short, char |
long | byte, short, char, int |
float | byte, short, char, int, long |
double | byte, short, char, int, long, float |
- Between integral types, the high bits are discarded:
(byte) 200is −56, and(int) 3_000_000_000Lis −1,294,967,296. - From
floatordoubletointorlong, the value is rounded towards zero; NaN becomes 0, and values too large or too small become the type’s largest or smallest value:(int) 1e20is 2,147,483,647. - To
byte,shortorchar, a floating-point value is first converted tointas above and then narrowed, so(byte) 1e20is −1. doubletofloatrounds to nearest, and may give zero or infinity.
Reference conversions
A widening reference conversion goes from a type to any of its
supertypes and needs no check at run time: ArrayList<String> to
List<String>, String[] to Object[], any
reference type to Object, and the null type to any reference type.
A narrowing reference conversion goes the other way, or between
types that could have a common subtype (such as a non-final class and an interface).
It needs a cast, and is checked at run time: if the object is not an instance of the
target, a ClassCastException is thrown. When the target is a
parameterised type the check is only partial, because type arguments are erased, and
the compiler gives an unchecked warning:
Object o = "text";
String s = (String) o; // checked: succeeds
Integer i = (Integer) o; // compiles, throws ClassCastException
List<?> any = List.of(1, 2);
List<String> strs = (List<String>) any; // unchecked warning: not checked at run time
Boxing and unboxing
Boxing (Java 5) converts a primitive to its wrapper, and unboxing the other way:
| Primitive | Wrapper | Cached by valueOf |
|---|---|---|
boolean | Boolean | true, false |
byte | Byte | all values |
char | Character | \u0000 to \u007f |
short | Short | −128 to 127 |
int | Integer | −128 to 127 |
long | Long | −128 to 127 |
float | Float | none required |
double | Double | none required |
Boxing is done by the wrapper’s valueOf method. JLS 5.1.7 requires
that boxing a constant true or false, a char up
to \u007f or an integer from −128 to 127 always gives the same object, so
Integer a = 127, b = 127; gives a == b true, while for 128 the
result is not defined. Compare wrappers with equals. Unboxing
null throws NullPointerException:
Map<String, Integer> counts = new HashMap<>();
int n = counts.get("missing"); // NullPointerException: get returned null
Unchecked, capture and string conversion
- Unchecked conversion turns a raw type into any parameterised type
of the same class:
ListtoList<String>. It is allowed for compatibility with code written before generics, with a warning unless every type argument is an unbounded wildcard. - Capture conversion replaces each wildcard in a type with a fresh
type variable, so that the compiler can reason about an expression of type
List<?>. These variables appear in error messages asCAP#1. See Generics. - String conversion turns any value into a
String:nullbecomes"null", a primitive is formatted as byString.valueOf, and any other object by calling itstoString()(or"null"if that returns null).
Contexts
| Context | Where | Allowed conversions |
|---|---|---|
| Assignment (5.2) | Assignments, variable initialisers, return, array initialiser elements, switch expression results | Identity, widening primitive and reference, boxing then widening reference, unboxing then widening primitive, unchecked; and narrowing of constants (below) |
| Strict invocation (5.3) | Method and constructor arguments, first phase of overload resolution | Identity, widening primitive, widening reference |
| Loose invocation (5.3) | Arguments, when no method applies strictly | As assignment, but no narrowing of constants |
| String (5.4) | An operand of + when the other is a String | String conversion |
| Casting (5.5) | The operand of a cast | Almost all: also narrowing primitive and narrowing reference, and unboxing after a narrowing reference |
| Numeric (5.6) | Operands of arithmetic, comparison and bitwise operators, array dimensions and indexes, numeric operands of ? : and switch expressions | Unboxing, then numeric promotion (below) |
| Testing (5.7) | The operand of instanceof with a pattern, a switch selector with patterns | Identity, widening reference, and narrowing reference only when checked; for primitives, identity only (the preview in Java 27 widens this) |
Narrowing of constants in assignment
In an assignment context only, a
constant expression
of type byte, short, char or int
may be narrowed to byte, short or char (or
boxed to Byte, Short or Character) if its value
fits:
byte b = 42; // fine: 42 fits in a byte
char c = 65; // fine: 'A'
byte big = 200; // error: 200 does not fit
int i = 42;
byte b2 = i; // error: i is not a constant
final int K = 42;
byte b3 = K; // fine: K is a constant variable
Long boxed = 42; // error: int cannot be boxed to Long
This does not apply to method arguments, so m(12) does not call
m(byte).
Numeric promotion
Before arithmetic, the operands are unboxed and then widened to a common type (JLS 5.6):
- If either operand is
double, both becomedouble. - Otherwise, if either is
float, both becomefloat. - Otherwise, if either is
long, both becomelong. - Otherwise both become
int, even if both werebyte,shortorchar.
Unary +, - and ~, the shift operators (each
operand separately), array indexes and dimensions promote a single operand the same
way, so -b for a byte b is an int.
The conditional operator ? : and switch expressions used as
standalone numeric expressions are a numeric choice context, with one
difference: if one operand is byte, short or
char and the others are constants that fit that type, the result keeps
the smaller type. So flag ? 'a' : 0 is a char.
Contexts and overload resolution
To choose between overloaded methods, the compiler tries three phases in turn and stops at the first that finds an applicable method (JLS 15.12.2):
- strict invocation: no boxing, no unboxing, no varargs;
- loose invocation: boxing and unboxing allowed, no varargs;
- variable arity: varargs methods too.
Within a phase, the most specific method is chosen. This keeps code written before Java 5 calling the same methods after boxing and varargs were added:
static void f(long x) { System.out.println("long"); }
static void f(Integer x) { System.out.println("Integer"); }
static void f(int... x) { System.out.println("varargs"); }
f(1); // prints "long": widening is found in phase 1, before boxing