Java не может сериализовать объекты, которые содержат TreeMaps с компараторами

В качестве задания (для курса ООП от Uni) у меня есть довольно большой проект: школьный реестр, где ученики могут видеть свои оценки, учителя могут добавлять оценки и так далее.

"Базовый" класс - это синглтон, который содержит все используемые классы (Java), такие как массив пользователей, классы (как в школьных классах) и TreeMap, который связывает классы и преподавателей с курсами.

Я хочу сериализовать этот базовый класс (Центральный), чтобы сохранить измененные данные. Проблема в том, что я получаю это исключение

java.io.NotSerializableException: liceu.Central$1
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1183)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1547)
at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:440)
at java.util.TreeMap.writeObject(TreeMap.java:2265)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1495)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1431)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1177)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1547)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1508)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1431)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1177)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347)
at liceu.Main.main(Main.java:31)

Все мои классы реализуют Serializable, и у них нет переходных или статических полей (за исключением синглтона, который имеет переменную экземпляра и метод getInstance в качестве статики).

Поскольку было бы достаточно много кода для публикации (и я рискнул бы аннулировать свое назначение, опубликовав его перед отправкой), я попытался доказать концепцию, пытаясь изолировать ошибку.

public class Central implements Serializable
{
    private ArrayList <User> users;
    private ArrayList <Class> classess;
    private TreeMap <Course, TreeMap <Class, Professor>> reunite;
    private static Central instance = null;

    private Central()
    {
        users = new ArrayList<>();
        classess = new ArrayList<>();
        reunite = new TreeMap<>(new Comparator<Student>(){
            @Override
            public int compare(Student e1, Student e2)
            {
                return e1.getName().compareTo(e2.getName());
            }
        });
    }
}

Если я оставлю только первые 2 ArrayLists, процесс сериализации будет работать. Проблема с TreeMap.

Является ли класс TreeMap сериализуемым? (В общем) Это из-за анонимного компаратора?

Вот основной класс с сериализацией

public class Main
{
    public static void main(String args[])
    {
        Central cent = Central.getInstance();
        FileOutputStream fos;
        ObjectOutputStream oos;

        cent.addUser(new Student(3,"id","pass","name","surname"));
        cent.addUser(new Student(3,"id2","pass","name","surname"));
        cent.addUser(new Student(3,"id1","pass","name","surname"));
        try
        {
            fos = new FileOutputStream("save.txt");
            oos = new ObjectOutputStream(fos);
            oos.writeObject(cent);
        }

        catch(IOException e)
        {
            e.printStackTrace();
        }
    }
}

3 ответа

Решение

TreeMap содержит ссылку на компаратор, который он использует для сравнения ключей. И компаратор является экземпляром анонимного класса, который не сериализуем. Таким образом, вы получите это исключение.

Преобразуйте анонимный компаратор в высокоуровневый или именованный внутренний класс, который также реализует Serializable.

Еще один способ заставить Comparator быть Serializable это преобразовать его в лямбду с дополнительным приведением привязки:

TreeMap<Integer, String> sortedMap = new TreeMap<>(
    (Comparator<Integer> & Serializable) (o1, o2) -> {
        return o1.compareTo(o2);
    }
);

Вот объяснение: лямбда-выражения Java, приведение и компараторы.

Добавьте "имплементации Serializable" в ваш Comparator, и это должно решить проблему.

Другие вопросы по тегам