For a class I was assigned to write code to read objects of the class Vehicle
using ObjectInputStream (in
). The objects are stored in an ArrayList called orders
.
SSCE:
// Read all orders
Object obj = in.readObject();
orders = (ArrayList<Vehicle>) obj;
However, the compiler complains:
MacBook:Homework Brienna$ javac Orders.java -Xlint:unchecked
Orders.java:152: warning: [unchecked] unchecked cast
orders = (ArrayList<Vehicle>) in.readObject();
^
required: ArrayList<Vehicle>
found: Object
1 warning
I always try to improve my code instead of ignoring or suppressing warnings. In this case, I have come up with a solution, but I'm trying to understand why it works, and if there is a better solution.
This update stops the warning:
// Read all orders, casting each order individually
Object obj = in.readObject();
ArrayList ar = (ArrayList) obj;
for (Object x : ar) {
orders.add((Vehicle) x);
}
Based on what I understand from what I've been reading, it works because (ArrayList<Vehicle>) obj
may throw an exception if not all the elements are Vehicle
. I am confused -- non-Vehicle objects can be added to the ArrayList even if its type parameter has been specified as Vehicle
? Also, is there a better solution, e.g. using instanceof
?
1 Answer 1
In general, the Java compiler knows the type of each variable, at every point during the execution. And when you operate on incompatible types, the program won't compile.
When you cast an object to ArrayList<Vehicle>
, the Java runtime environment can only ensure that the cast to ArrayList<something>
succeeds, but due to backwards compatibility (Java 1.4 from 2002), the cast will succeed, no matter if the arraylist contains Integer
or String
or Vehicle
objects. Therefore, in this situation, the compiler warning tells you: Hey, you are doing something that cannot be checked, make sure you know what you are doing.
Your code that first deserializes the list as a raw type (ArrayList
instead of ArrayList<Vehicle>
) doesn't produce the compiler warning since the raw ArrayList
is equivalent to ArrayList<Object>
, and you won't get a surprising ClassCastException
when working with this list.
If you are sure that the serialized list will only ever contain objects of type Vehicle
, you can safely ignore that warning. In my code, I have written a utility function like this:
@SuppressWarnings("unchecked")
public static <T> T castToAnything(Object obj) {
return (T) obj;
}
...
ArrayList<Vehicle> vehicles = castToAnything(in.readObject());
Another way is to not serialize the list, but to create a wrapper class:
public class SerializedData implements Serializable {
public final ArrayList<Vehicle> vehicles = new ArrayList<>();
}
When you serialize this class and deserialize it, you still have to cast the type using (SerializedData) in.readObject()
, but the warning goes away.
This still doesn't guarantee that the vehicles
list contains only vehicles after deserialization, but that is an entirely different problem.
Collection
with generics parameter since the at runtime the generics information is not available (due to generics erasure) and therefore the JVM cannot check that all elements in thatCollection
to be of that generics type. \$\endgroup\$