ZetCode

Java Type Conversions

Last modified: May 7, 2025

Type conversion (type casting) in Java means changing a value from one data type to another. This is needed when you want to use data in a different format, assign it to a variable of another type, or pass it to a method expecting a different type. Java supports several conversion methods for both primitive and object types.

Understanding type conversions helps you avoid runtime errors and data loss in Java programs.

Primitive Type Conversions

Java's primitive types (byte, short, int, long, float, double, char, boolean) can be converted to each other. There are two main types: implicit (widening) and explicit (narrowing).

Implicit Conversion (Widening or Automatic Type Conversion)

Widening conversion happens when a smaller type is converted to a larger type. Java does this automatically because it is safe and there is no data loss.

The typical widening conversion order is:

byte -> short -> int -> long -> float -> double

Also, char can be widened to int, long, float, or double.

WideningConversionDemo.java
package com.zetcode;
public class WideningConversionDemo {
 public static void main(String[] args) {
 int myInt = 100;
 long myLong = myInt; // int to long (implicit)
 float myFloat = myLong; // long to float (implicit)
 double myDouble = myFloat; // float to double (implicit)
 System.out.println("Original int: " + myInt);
 System.out.println("Widened to long: " + myLong);
 System.out.println("Widened to float: " + myFloat);
 System.out.println("Widened to double: " + myDouble);
 char myChar = 'A';
 int charToInt = myChar; // char to int (implicit)
 System.out.println("Original char: " + myChar);
 System.out.println("Widened char to int: " + charToInt); // Prints 65
 }
}

In this example, values are promoted to a larger type automatically, without explicit casting.

Explicit Conversion (Narrowing or Manual Type Conversion)

Narrowing conversion happens when a larger type is converted to a smaller type. This must be done explicitly with a cast operator (targetType) because it can cause data or precision loss.

The typical narrowing conversion order is the reverse of widening:

double -> float -> long -> int -> short -> char -> byte
NarrowingConversionDemo.java
package com.zetcode;
public class NarrowingConversionDemo {
 public static void main(String[] args) {
 double myDouble = 123.456;
 float myFloat = (float) myDouble; // double to float (explicit)
 long myLong = (long) myFloat; // float to long (explicit, fractional part lost)
 int myInt = (int) myLong; // long to int (explicit)
 short myShort = (short) myInt; // int to short (explicit, potential overflow)
 byte myByte = (byte) myShort; // short to byte (explicit, potential overflow)
 char myChar = (char) myInt; // int to char (explicit, takes lower 16 bits)
 System.out.println("Original double: " + myDouble);
 System.out.println("Narrowed to float: " + myFloat);
 System.out.println("Narrowed to long: " + myLong);
 System.out.println("Narrowed to int: " + myInt);
 System.out.println("Narrowed to short: " + myShort);
 System.out.println("Narrowed to byte: " + myByte);
 System.out.println("Narrowed int to char: " + myChar);
 // Example of data loss due to overflow
 int largeInt = 300;
 byte smallByte = (byte) largeInt; // 300 is 100101100 in binary. Byte takes last 8 bits.
 // 300 % 256 = 44.
 System.out.println("Original large int: " + largeInt);
 System.out.println("Narrowed large int to byte: " + smallByte); // Output: 44
 double preciseDouble = 99.99;
 int truncatedInt = (int) preciseDouble;
 System.out.println("Original precise double: " + preciseDouble);
 System.out.println("Narrowed to int (truncation): " + truncatedInt); // Output: 99
 }
}

When narrowing, higher-order bits may be lost if the value is out of range for the target type. For floating-point to integer, the fractional part is simply removed.

Type Promotion in Expressions

Java promotes smaller types to larger ones in expressions to prevent data loss during calculations. The rules are:

  • If one operand is double, the other is converted to double.
  • Else, if one operand is float, the other is converted to float.
  • Else, if one operand is long, the other is converted to long.
  • Otherwise (byte, short, char, int), both operands are converted to int.
ExpressionPromotionDemo.java
package com.zetcode;
public class ExpressionPromotionDemo {
 public static void main(String[] args) {
 byte b1 = 10;
 byte b2 = 20;
 // byte resultByte = b1 + b2; // Error: b1 + b2 results in an int
 int resultInt = b1 + b2; // Correct: byte + byte -> int
 byte resultByteCasted = (byte) (b1 + b2); // Explicit cast needed for byte result
 System.out.println("b1 + b2 (as int): " + resultInt);
 System.out.println("b1 + b2 (casted to byte): " + resultByteCasted);
 short s = 5;
 float f = 10.5f;
 // short shortResult = s + f; // Error: s + f results in a float
 float floatResult = s + f; // Correct: short + float -> float
 System.out.println("s + f (as float): " + floatResult);
 int i = 100;
 double d = 20.75;
 // int intResultSum = i + d; // Error: i + d results in double
 double doubleResult = i + d; // Correct: int + double -> double
 System.out.println("i + d (as double): " + doubleResult);
 }
}

Object Type Conversions (Casting)

For object types (classes and interfaces), casting lets you treat an object as another type within its inheritance tree.

Upcasting (Implicit)

Upcasting means treating a subclass object as its superclass. This is always safe and automatic because a subclass is a kind of its superclass.

ObjectCastingDemo.java
package com.zetcode.casting;
class Animal {
 void makeSound() {
 System.out.println("Generic animal sound");
 }
}
class Dog extends Animal {
 @Override
 void makeSound() {
 System.out.println("Woof woof");
 }
 void fetch() {
 System.out.println("Dog is fetching.");
 }
}
class Cat extends Animal {
 @Override
 void makeSound() {
 System.out.println("Meow");
 }
 void scratch() {
 System.out.println("Cat is scratching.");
 }
}
public class ObjectCastingDemo {
 public static void main(String[] args) {
 // Upcasting
 Dog myDog = new Dog();
 Animal myAnimalFromDog = myDog; // Implicit upcasting (Dog to Animal)
 Cat myCat = new Cat();
 Animal myAnimalFromCat = myCat; // Implicit upcasting (Cat to Animal)
 System.out.print("myAnimalFromDog (originally Dog) says: ");
 myAnimalFromDog.makeSound(); // Calls Dog's overridden makeSound()
 System.out.print("myAnimalFromCat (originally Cat) says: ");
 myAnimalFromCat.makeSound(); // Calls Cat's overridden makeSound()
 // myAnimalFromDog.fetch(); // Compile error: Animal type doesn't have fetch() method
 // The reference type determines accessible methods.
 // Using an array of superclass type
 Animal[] animals = new Animal[2];
 animals[0] = new Dog(); // Upcasting
 animals[1] = new Cat(); // Upcasting
 for (Animal animal : animals) {
 animal.makeSound(); // Polymorphism in action
 }
 }
}

When upcasting, you can only use methods and fields from the superclass (or those overridden by the subclass). Subclass-specific methods are not accessible through the superclass reference.

Downcasting (Explicit)

Downcasting means treating a superclass object as a subclass. This is risky and must be explicit, usually after checking with instanceof to avoid ClassCastException.

ObjectDowncastingDemo.java
package com.zetcode.casting; // Assuming Animal, Dog, Cat are in the same package or imported
public class ObjectDowncastingDemo {
 public static void main(String[] args) {
 Animal myAnimal = new Dog(); // Upcasted to Animal, actual object is Dog
 // Animal anotherAnimal = new Animal(); // Actual object is Animal
 // Attempting to downcast myAnimal to Dog
 if (myAnimal instanceof Dog) {
 Dog myDog = (Dog) myAnimal; // Explicit downcasting
 System.out.print("Downcasted Dog object says: ");
 myDog.makeSound(); // Calls Dog's makeSound()
 myDog.fetch(); // Now fetch() is accessible
 } else {
 System.out.println("Cannot downcast myAnimal to Dog.");
 }
 Animal generalAnimal = new Animal();
 // Attempting to downcast generalAnimal to Dog (will fail if not checked)
 if (generalAnimal instanceof Dog) {
 Dog specificDog = (Dog) generalAnimal; // This line won't be reached
 specificDog.fetch();
 } else {
 System.out.println("generalAnimal is not an instance of Dog. Cannot downcast.");
 }
 // Example leading to ClassCastException
 Animal possiblyCat = new Cat();
 // Dog notADog = (Dog) possiblyCat; // This would throw ClassCastException if executed
 // because a Cat is not a Dog.
 try {
 Animal anAnimal = new Cat(); // Actual object is Cat
 Dog aDog = (Dog) anAnimal; // Attempting to cast Cat to Dog
 aDog.fetch();
 } catch (ClassCastException e) {
 System.err.println("Error during downcasting: " + e.getMessage());
 }
 }
}

The instanceof operator checks if an object is a certain type or a subclass of it. Always use instanceof before downcasting to avoid errors.

Conversions Involving Strings

Strings are objects in Java. Converting between strings and primitives or other objects is common.

Primitive to String

  • Using String.valueOf: This method is overloaded for all primitive types (e.g., String.valueOf(int), String.valueOf(double)).
  • Using wrapper class toString methods: E.g., Integer.toString(int), Double.toString(double).
  • Concatenation with an empty string: E.g., "" + myInt. While simple, this can be less efficient due to string concatenation overhead.

String to Primitive

This is usually done with parseXxx methods of wrapper classes (e.g., Integer.parseInt(String)). If the string is not valid, a NumberFormatException is thrown.

Object to String

  • Every object has a toString method (inherited from Object class). It's good practice to override toString in your classes to provide a meaningful string representation.
  • String.valueOf(Object obj): This method internally calls obj.toString if obj is not null. If obj is null, it returns the string "null" (avoiding a NullPointerException).
StringConversionDemo.java
package com.zetcode;
import java.util.Date;
public class StringConversionDemo {
 public static void main(String[] args) {
 // Primitive to String
 int num = 123;
 String strNum1 = String.valueOf(num);
 String strNum2 = Integer.toString(num);
 String strNum3 = "" + num;
 System.out.println("Primitive to String: " + strNum1 + ", " + strNum2 + ", " + strNum3);
 double val = 45.67;
 String strVal = String.valueOf(val);
 System.out.println("Double to String: " + strVal);
 // String to Primitive
 String strInt = "100";
 String strDouble = "200.50";
 String strInvalid = "abc";
 try {
 int parsedInt = Integer.parseInt(strInt);
 double parsedDouble = Double.parseDouble(strDouble);
 System.out.println("String to int: " + parsedInt);
 System.out.println("String to double: " + parsedDouble);
 // int invalidParse = Integer.parseInt(strInvalid); // This would throw NumberFormatException
 } catch (NumberFormatException e) {
 System.err.println("Error parsing string: " + e.getMessage());
 }
 // Boolean parsing
 String trueStr = "true";
 String falseStr = "FalSe"; // Case-insensitive for "true"
 boolean bTrue = Boolean.parseBoolean(trueStr); // true
 boolean bFalse = Boolean.parseBoolean(falseStr); // false (only "true" ignoring case is true)
 System.out.println("String 'true' to boolean: " + bTrue);
 System.out.println("String 'FalSe' to boolean: " + bFalse);
 // Object to String
 Date today = new Date();
 String dateStr = today.toString(); // Uses Date's overridden toString()
 System.out.println("Date object to String: " + dateStr);
 Object nullObj = null;
 String nullStr = String.valueOf(nullObj); // Safely handles null, returns "null"
 System.out.println("String.valueOf(nullObj): " + nullStr);
 // String problematicNullStr = nullObj.toString(); // This would throw NullPointerException!
 }
}

Autoboxing and Unboxing

Autoboxing is when Java automatically converts primitives to their wrapper classes (e.g., int to Integer). Unboxing is the reverse. This makes code simpler and more readable.

This feature lets you write Integer myIntegerObject = 100; instead of Integer myIntegerObject = new Integer(100); (the constructor is deprecated; use valueOf instead).

AutoboxingUnboxingDemo.java
package com.zetcode;
import java.util.ArrayList;
import java.util.List;
public class AutoboxingUnboxingDemo {
 public static void main(String[] args) {
 // Autoboxing: primitive int to Integer object
 Integer integerObject = 100; // Compiler converts to Integer.valueOf(100)
 System.out.println("Autoboxed Integer object: " + integerObject);
 // Unboxing: Integer object to primitive int
 int primitiveInt = integerObject; // Compiler converts to integerObject.intValue()
 System.out.println("Unboxed primitive int: " + primitiveInt);
 // Example in collections
 List<Integer> numberList = new ArrayList<>();
 numberList.add(10); // Autoboxing: 10 (int) becomes Integer.valueOf(10)
 numberList.add(20);
 int firstElement = numberList.get(0); // Unboxing: Integer object from list to int
 System.out.println("First element from list (unboxed): " + firstElement);
 // Pitfall: NullPointerException during unboxing
 Integer nullInteger = null;
 try {
 // int problematicInt = nullInteger; // This would throw NullPointerException
 // as it tries to call nullInteger.intValue()
 if (nullInteger != null) { // Always check for null before unboxing
 int safeInt = nullInteger;
 System.out.println("Safely unboxed: " + safeInt);
 } else {
 System.out.println("Cannot unbox a null Integer object.");
 }
 } catch (NullPointerException e) {
 System.err.println("Error during unboxing: " + e.getMessage());
 }
 }
}

While convenient, autoboxing/unboxing can create unnecessary objects in performance-critical code. Unboxing a null wrapper object causes a NullPointerException.

Summary of Key Conversions & Potential Issues

Conversion Type Description Mechanism Potential Issues
Primitive Widening Smaller to larger numeric type (e.g., int to long) Implicit (Automatic) None (Safe)
Primitive Narrowing Larger to smaller numeric type (e.g., double to int) Explicit Cast (type)value Data loss, precision loss, overflow
Object Upcasting Subclass to Superclass/Interface Implicit (Automatic) None (Safe, but loses access to subclass-specific members via superclass reference)
Object Downcasting Superclass/Interface to Subclass Explicit Cast (SubclassType)object ClassCastException if object is not an instance of target type
Primitive to String e.g., int to String String.valueOf, Wrapper.toString, "" + primitive Generally safe
String to Primitive e.g., String to int Wrapper.parseXxx NumberFormatException if string format is invalid
Object to String Any object to String obj.toString, String.valueOf(obj) NullPointerException if obj.toString is called on null (String.valueOf handles null safely)
Autoboxing Primitive to Wrapper (e.g., int to Integer) Implicit (Automatic) Performance overhead in loops if not careful
Unboxing Wrapper to Primitive (e.g., Integer to int) Implicit (Automatic) NullPointerException if wrapper object is null

Best Practices for Type Conversion

  • Prefer Widening: Use widening conversions whenever possible as they are safe and automatic.
  • Be Cautious with Narrowing: Understand the potential for data loss or overflow. Cast explicitly and ensure the value is within the target type's range if critical.
  • Use instanceof for Downcasting: Always check with instanceof before downcasting objects to prevent ClassCastException.
  • Handle Exceptions: When parsing strings to numbers (parseXxx), be prepared to catch NumberFormatException.
  • Null Checks: Before unboxing wrapper objects or calling methods like toString on an object reference, check for null to avoid NullPointerException. String.valueOf(Object) is safer for converting objects to strings when nulls are possible.
  • Override toString: Provide meaningful string representations for your custom classes by overriding the toString method.
  • Understand Autoboxing: Be aware of where autoboxing and unboxing occur, especially in performance-sensitive code or when dealing with null wrapper objects.

Source

Official Java Primitive Data Types and Conversions

Java's type conversion system is flexible, allowing data to move between primitive and object types. Implicit conversions are seamless, but explicit conversions require care to avoid data loss or runtime errors. Knowing the rules and pitfalls helps you write safer, more reliable Java code.

Author

My name is Jan Bodnar. I am a programmer with many years of experience. Since 2007, I have written over 1,400 articles and eight e-books. With more than eight years of teaching, I enjoy sharing knowledge and helping others learn programming.

List all Java tutorials.

AltStyle によって変換されたページ (->オリジナル) /