JB Header

Type Conversion and Casting in Java with Examples

Introduction Programs constantly mix types - an int count multiplied by a double price, or a long timestamp stored in an int. This tutorial explains type conversion and casting in Java. We will start with widening conversions, which Java does automatically. We will then look at narrowing conversions, which need an explicit cast, and see exactly what happens to values that do not fit. Next we will learn the rules of numeric promotion in expressions, which explain several surprising results. Lastly, we will convert between numbers and strings, which is not done by casting at all.

(Note - This tutorial builds on Java Primitive Data TypesClick to read tutorial: Java Primitive Data Types and Their Ranges with Examples, which lists the size and range of each type.) Widening conversion - automatic A widening conversion converts a value to a type which can hold every value of the original type. Java does it automatically, because nothing is lost. The widening order of the numeric types is -
byte → short → int → long → float → double
and char can be widened to int and the types after it. Any type in the chain can be assigned to a type further to its right without a cast.
Widening conversions happen automatically
//Widening.java
public class Widening {
  public static void main(String[] args) {
    int count = 42;
    long bigCount = count;          // int -> long
    double average = count;         // int -> double
    char letter = 'A';
    int code = letter;              // char -> int
    System.out.println(bigCount + " " + average + " " + code);

    long big = 123_456_789_123L;
    float f = big;                  // long -> float: allowed, but may lose precision
    System.out.println(big + " as float = " + f);
  }
}
 OUTPUT of the above code
42 42.0 65
123456789123 as float = 1.2345679E11
Explanation of the code
  • No casts were needed. average printed as 42.0 - the value is the same, now stored as a double.
  • char to int gives the character's Unicode number, 65.
  • Watch out: long to float (and int or long to double for very large values) is a widening conversion, but it can lose precision. A float keeps only about 7 significant digits, so 123456789123 became 1.2345679E11 - about 123456790000. The range fits; the exact digits do not.
Narrowing conversion - explicit casting A narrowing conversion goes the other way - from a type to one which cannot hold all of its values, such as double to int. Because information may be lost, Java will not do it automatically -
Narrowing without a cast does not compile
//NoCast.java
public class NoCast {
  public static void main(String[] args) {
    double price = 9.99;
    int whole = price;
  }
}
 OUTPUT on compiling the above code
NoCast.java:5: error: incompatible types: possible lossy conversion from double to int
    int whole = price;
                ^
1 error
To tell the compiler that you accept the loss, write a cast - the target type in parentheses before the value -
int whole = (int) price;
Narrowing conversions with casts
//Narrowing.java
public class Narrowing {
  public static void main(String[] args) {
    double price = 9.99;
    System.out.println("(int) 9.99    = " + (int) price);
    System.out.println("(int) -9.99   = " + (int) -9.99);
    System.out.println("Math.round    = " + Math.round(price));

    int big = 300;
    System.out.println("(byte) 300    = " + (byte) big);

    long tooBig = 3_000_000_000L;
    System.out.println("(int) 3 billion = " + (int) tooBig);

    double huge = 1e20;
    System.out.println("(int) 1e20    = " + (int) huge);

    int code = 66;
    System.out.println("(char) 66     = " + (char) code);
  }
}
 OUTPUT of the above code
(int) 9.99    = 9
(int) -9.99   = -9
Math.round    = 10
(byte) 300    = 44
(int) 3 billion = -1294967296
(int) 1e20    = 2147483647
(char) 66     = B
Explanation of the code
  • Floating-point to integer: the fractional part is cut off (truncated towards zero), not rounded. 9.99 becomes 9, and -9.99 becomes -9. Use Math.round() to round to the nearest whole number.
  • Integer to a smaller integer: only the lowest bits are kept, so the value can change completely. 300 does not fit in a byte (-128 to 127), and becomes 44. 3 billion does not fit in an int, and becomes a negative number.
  • Floating-point too large for the integer type: the result is the largest (or smallest) value of the target type, so (int) 1e20 gives Integer.MAX_VALUE.
  • (char) 66 converts a Unicode number back to its character, B.
Note - A cast never fails or throws an error for primitive numbers - it always produces some value. Before narrowing, make sure the value fits, or use methods such as Math.toIntExact(), which throws an exception instead of silently changing the value.
Numeric promotion in expressions When an expression combines values, Java first converts them to a common type, following these rules of binary numeric promotion -
  1. If either value is a double, the other is converted to double.
  2. Otherwise, if either is a float, the other becomes float.
  3. Otherwise, if either is a long, the other becomes long.
  4. Otherwise, both become int - even if they are both byte, short or char.
These rules explain several common surprises -
Numeric promotion in expressions
//Promotion.java
public class Promotion {
  public static void main(String[] args) {
    int total = 7, count = 2;
    System.out.println("7 / 2          = " + (total / count));
    System.out.println("7 / 2.0        = " + (total / 2.0));
    System.out.println("(double) 7 / 2 = " + ((double) total / count));
    System.out.println("(double)(7 / 2)= " + (double) (total / count));

    byte a = 10, b = 20;
    byte sum = (byte) (a + b);      // a + b is an int, so a cast is needed
    System.out.println("byte sum       = " + sum);

    char c = 'A';
    System.out.println("'A' + 1        = " + (c + 1));
    c += 1;                         // compound assignment casts automatically
    System.out.println("c after += 1   = " + c);
  }
}
 OUTPUT of the above code
7 / 2          = 3
7 / 2.0        = 3.5
(double) 7 / 2 = 3.5
(double)(7 / 2)= 3.0
byte sum       = 30
'A' + 1        = 66
c after += 1   = B
Explanation of the code
  • 7 / 2 is 3: both values are int, so integer division is used and the remainder is dropped. Making either value a double - 7 / 2.0, or casting one value with (double) total / count - gives 3.5.
  • (double) (7 / 2) gives 3.0, not 3.5. The division inside the parentheses happens first, with ints, and only its result is cast. Cast a value before the division, not the result after it.
  • a + b is an int, even though a and b are bytes, so assigning it to a byte needs a cast.
  • c + 1 is the int 66, but c += 1 gives the char B. Compound assignment operators such as += include a hidden cast back to the variable's type. See assignment operatorsClick to read tutorial: Java Assignment, Increment/Decrement and Ternary Operators for more.
Converting between numbers and strings You cannot cast between a String and a number - (int) "42" does not compile. Use methods of the wrapper classes and String instead.
Converting between strings and numbers
//StringConversion.java
public class StringConversion {
  public static void main(String[] args) {
    int quantity = Integer.parseInt("42");
    double price = Double.parseDouble("19.99");
    System.out.println("total = " + quantity * price);

    String s1 = String.valueOf(quantity);
    String s2 = "" + price;
    System.out.println(s1 + " and " + s2 + " are Strings of length " + s1.length() + " and " + s2.length());

    int bad = Integer.parseInt("forty-two");
  }
}
 OUTPUT of the above code
total = 839.5799999999999
42 and 19.99 are Strings of length 2 and 5
Exception in thread "main" java.lang.NumberFormatException: For input string: "forty-two"
	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
	at java.base/java.lang.Integer.parseInt(Integer.java:564)
	at java.base/java.lang.Integer.parseInt(Integer.java:661)
	at StringConversion.main(StringConversion.java:12)
Explanation of the code
  • Integer.parseInt() and Double.parseDouble() convert text to numbers. Each wrapper class has a similar method, such as Long.parseLong(). (The long total, 839.5799999999999, is the floating-point rounding error described in primitive data typesClick to read tutorial: Java Primitive Data Types and Their Ranges with Examples.)
  • String.valueOf(), or joining a number to a string with +, converts a number to text.
  • Text which is not a valid number causes a NumberFormatException at runtime - so check or handle user input before parsing it.
Summary In this tutorial we first saw widening conversions, which Java performs automatically along the chain from byte to double, and noted that long to float can lose precision. We then used casts for narrowing conversions, and saw that floating-point values are truncated and that integers which do not fit lose their high bits. Next we learned the rules of numeric promotion, which explain integer division, why byte + byte is an int, and where to put a cast. Lastly, we converted between strings and numbers with parseInt(), parseDouble() and String.valueOf(). In the next tutorial, we will let the compiler work out types for us with the var keywordClick to read tutorial: Java var Keyword: Local Variable Type Inference.