简体   繁体   中英

Cannot cast from ArrayList<Object> to ArrayList<MyObject>

I'm trying to create a generic loadFile method that can read a serialized object from a file. However the object type may vary depending on the file to load. I obviously misunderstood how casting works but can't really find what I ought to be doing here.

Below is my FileAdapter class:

public class FileAdapter {

    public ArrayList<Transaction> loadTransactions() {

        return (ArrayList<Transaction>) loadFile(new File("data/transactions.ser"));
    }

    public ArrayList<Fund> loadFunds() {

        return (ArrayList<Fund>) loadFile(new File("data/funds.ser"));
    }

    private ArrayList<Object> loadFile(File file) {

        ArrayList<Object> obj = new ArrayList<>();

        FileInputStream fis = new FileInputStream(file);
        in = new ObjectInputStream(fis);
        obj = (ArrayList<Object>) in.readObject();

        return obj;
    }
}

As can be seen, I have two methods that load objects of different types from files but I want to share the code that actually loads the file. Is what I'm trying to do feasible but just wrongly implemented or can this not work at all if the types vary?

It could be done by defining a bounded type parameter in the definition of your method loadFile , as next:

public ArrayList<Transaction> loadTransactions(){
    return loadFile(new File("data/transactions.ser"));
}

public ArrayList<Fund> loadFunds(){
    return loadFile(new File("data/funds.ser"));
}

private <T> ArrayList<T> loadFile(File file) {
    ArrayList<T> obj = new ArrayList<>();
    ...
    return (ArrayList<T>) in.readObject();
}

If you want to be certain your code does not blindly proceed unless the List actually contains what you expect it to contain, pass the element type to your method, and use Class.cast to make sure every element is what you expect it to be:

public ArrayList<Transaction> loadTransactions() {
    return loadFile(new File("data/transactions.ser"), Transaction.class);
}

public ArrayList<Fund> loadFunds() {
    return loadFile(new File("data/funds.ser"), Fund.class);
}

private <T> ArrayList<T> loadFile(File file,
                                  Class<T> elementType) {
    ArrayList<?> obj;

    try (FileInputStream fis = new FileInputStream(file);
         ObjectInputStream in = new ObjectInputStream(fis)) {

        // This is safe, because it does not make any assumptions
        // about the ArrayList's generic type.
        obj = (ArrayList<?>) in.readObject();
    } catch (IOException | ClassNotFoundException e) {
        throw new RuntimeException(e);
    }

    ArrayList<T> typedList = new ArrayList<>(obj.size());
    for (Object element : obj) {
        typedList.add(elementType.cast(element));
    }

    return typedList;
}

If you ignore the compiler's “unsafe” warnings, you risk strange errors that occur at points far away from where the actual coding error was, which makes them very difficult to debug.

When you do (ArrayList<Object>) in.readObject() , if the object is not an ArrayList, you will immediately get a ClassCastException, which is a good thing. The exception prevents your code from proceeding on the incorrect assumption you have an ArrayList when something else was read.

But the generic type, be it <Object> or <Fund> or anything else, is information that does not exist at runtime, due to type erasure. So an unsafe generic cast does not throw any exception, regardless of what the ArrayList contains. Only later, when you try to read elements from the ArrayList, will a ClassCastException occur.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM