4
\$\begingroup\$

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?

\$\endgroup\$
  • 2
    \$\begingroup\$ You cannot casts a 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 that Collection to be of that generics type. \$\endgroup\$ – Timothy Truckle Dec 30 '17 at 11:56
3
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.