
1. Overview
In Java, arrays are a fundamental part of the language, providing a structured way to store multiple values of the same type. However, when working with arrays and type casting, we sometimes encounter unexpected runtime exceptions.
One such issue arises when we attempt to cast an Object[] array to a specific array type, such as Integer[]. This results in a ClassCastException, which can confuse many of us.
In this tutorial, we’ll explore why this happens, understand the underlying mechanics of Java arrays, and learn how to avoid such errors in our code.
2. Introduction to the Problem
As usual, let’s first understand the issue through an example:
Integer[] convertObjectArray() {
Object[] objArray = new Object[3];
objArray[0] = 1;
objArray[1] = 2;
objArray[2] = 3;
return (Integer[]) objArray;
}
We inserted three int values into an Object[] array in the above method. Since the Object[] array only contains integers, we attempt to convert it to an Integer[] array.
We can call this method in a test to see what will happen:
Exception ex = assertThrows(ClassCastException.class, () -> convertObjectArray());
LOG.error("The exception stacktrace:", ex);
As we can see, calling this method raises ClassCastException. Also, in the output, we can see the exception details:
java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [Ljava.lang.Integer; ...
at ...
at ...
...
The message is pretty straightforward. It says we cannot cast the Object[] array to an Integer[] array, although all elements in the array are Integers. Next, let’s understand why this issue occurs.
3. Why Does This Exception Occur?
To understand why this happens, we need to examine the behavior of arrays in Java, particularly array covariance and runtime type checking.
Covariance means that a subclass type can be substituted for its parent type in certain situations. In Java, arrays are covariant, meaning that an array of a subclass (Integer[]) can be assigned to an array of its superclass (Object[]).
This feature allows us to do something like this:
Object[] objArray = new Integer[5]; // Valid, because Integer[] is a subtype of Object[]
objArray[0] = 42; // Allowed, since 42 is an Integer
However, this doesn’t mean we can freely cast Object[] to Integer[]. The actual array type remains Object[], and Java doesn’t allow treating it as Integer[] at runtime.
Therefore, this attempt fails:
(Integer[]) objArray
At this point, Java checks if objArray is actually an Integer[]. Since its actual runtime type is Object[], the cast fails, resulting in a ClassCastException.
4. Resolving the Issue
Now that we understand why the problem occurs, let’s figure out how to solve it.
4.1. Using an Integer[] Array Directly
The best way to avoid this issue is to initialize an array with the correct type from the beginning. In our example, instead of creating an Object[] array, we can create an Integer[] array if we plan to store Integer values:
Integer[] getIntegerArray() {
Integer[] intArray = new Integer[3];
intArray[0] = 1;
intArray[1] = 2;
intArray[2] = 3;
return intArray;
}
A simple test can verify it works as expected:
assertArrayEquals(new Integer[] { 1, 2, 3 }, getIntegerArray());
Initializing an array with the desired type saves unnecessary conversions. However, we often receive Object[] arrays from other libraries or APIs. In this case, we cannot initialize the array from the beginning. Next, let’s see how to convert an Object[] array to an Integer[] array.
4.2. Stream-Based Conversion
We can leverage Stream API to convert Object[] to Integer[]:
Integer[] objArrayToIntArrayByStream() {
Object[] objArray = new Object[] { 1, 2, 3 };
Integer[] intArray = Stream.of(objArray).toArray(Integer[]::new);
return intArray;
}
In this example, toArray(Integer[]::new) collects the elements from the stream into a new Integer[] array. Let’s test it:
assertArrayEquals(new Integer[] { 1, 2, 3 }, objArrayToIntArrayByStream());
This approach does the job, but we must be careful with type safety. We need to ensure that all elements in the Object[] are Integer instances. Otherwise, we may encounter ClassCastException when it converts any non-Integer element to Integer.
4.3. Loop-Based Conversion
Alternatively, we can easily convert an Object[] array to an Integer[] array by a for loop:
Integer[] objArrayToIntArray() {
Object[] objArray = new Object[]{ 1, 2, 3 };
Integer[] intArray = new Integer[objArray.length];
for (int i = 0; i < objArray.length; i++) {
intArray[i] = (Integer) objArray[i];
}
return intArray;
}
This method solves the problem by reading each element from the Object[] array and adds it to the target Integer[] array in a loop:
assertArrayEquals(new Integer[] { 1, 2, 3 }, objArrayToIntArray());
Similarly, if Object[] contains non-Integer elements, ClassCastException may raise.
4.4. Creating a Generic Conversion Method
We can extend the loop-based conversion method to a generic method, so that we can convert an Object[] array to any typed array (T[]). Next, let’s look at how to implement such a method:
<T> T[] convertFromObjectArray(Class<T> clazz, Object[] objArray) {
T[] targetArray = (T[]) Array.newInstance(clazz, objArray.length);
for (int i = 0; i < objArray.length; i++) {
if (clazz.isInstance(objArray[i])) {
targetArray[i] = clazz.cast(objArray[i]);
} else {
throw new ClassCastException("Element #" + i + ": Cannot cast " + objArray[i].getClass()
.getName() + " to " + clazz.getName());
}
}
return targetArray;
}
In this implementation, we first initialized a generic array. Then, we explicitly check each element before casting and throw a well-defined ClassCastException with a clear message.
Now, we can use this method to convert an Object[] to an array of different types:
assertArrayEquals(new Integer[] { 1, 2, 3 }, convertFromObjectArray(Integer.class, new Object[] { 1, 2, 3 }));
assertArrayEquals(new String[] { "I'm Kai", "I'm Liam", "I'm Kevin" },
convertFromObjectArray(String.class, new Object[] { "I'm Kai", "I'm Liam", "I'm Kevin" }));
Of course, in case the input Object[] array contains elements in mixed types, the ClassCastException will be thrown:
Exception ex = assertThrows(ClassCastException.class, () -> convertFromObjectArray(String.class, new Object[] { "I'm Kai", Instant.now(), "I'm Kevin" }));
assertEquals("Element #1: Cannot cast java.time.Instant to java.lang.String", ex.getMessage());
As the test shows, the ClassCastException has a meaningful message, which helps debug and identify which specific element caused the issue.
5. Conclusion
In this article, we’ve discussed why the ClassCastException is raised when casting Object[] to Integer[] in Java. We also explored a few approaches to solving this issue through examples.
As always, the complete source code for the examples is available over on GitHub.
The post Resolving ClassCastException: Ljava.lang.Object; cannot be cast to Ljava.lang.Integer first appeared on Baeldung.