Сериализация динамически сгенерированного класса

Предположим, что у меня есть абстрактный класс

public abstract class Base implements Serializable {
    static final long serialVersionUID = 1;
    public Base(int x) { ... }
    public abstract void baseMethod();
}

а затем я динамически создаю класс

public class Temp {
    public Base getBase() {
        return new Base(2) {

            static final long serialVersionUID = 1;

            @Override
            public void baseMethod() { .... } 
        };
    }
}

Я могу динамически генерировать класс Temp из строки, вызывать новый экземпляр, извлекать базовый объект и использовать его как любой другой экземпляр, расширяющий базу. Проблема возникает, когда я сериализую. Я могу сериализовать базовый объект Base, который я извлекаю из динамического класса Temp, но затем я не могу десериализовать позже в другом сеансе, потому что Temp больше не находится на пути к классу.

Ребята, вы можете придумать какой-нибудь способ обойти это? Должен быть способ получить рассматриваемый базовый объект отдельно от класса Temp (о котором я никогда не забочусь после того, как извлек из него базовый класс). Объект Base не зависит ни от чего в классе Temp.


person richard    schedule 23.02.2013    source источник
comment
I am able to generate the class Temp from a String dynamically. Как вы создаете объект Temp из String?   -  person Vishal K    schedule 23.02.2013
comment
Что вы используете для сериализации?   -  person stan    schedule 23.02.2013
comment
@VishalK, я передаю строку второго блока кода в OP в какой-то другой код, который обрабатывает ее как файл .java и компилирует в класс. Затем я вызываю класс и получаю то, что мне нужно. Все это прекрасно работает, я могу без проблем использовать возвращаемый объект Base с помощью getBase().   -  person richard    schedule 23.02.2013
comment
@StanislavPalatnik, просто интерфейс сериализации Java.   -  person richard    schedule 23.02.2013
comment
Вы можете создать метод в классе Temp, где вы десериализуете объект базового класса. Таким образом, вы можете десериализовать объект базового класса в любом объекте Temp.   -  person Vishal K    schedule 23.02.2013
comment
@VishalK, я не думаю, что ты понимаешь, что я здесь делаю (что, по общему признанию, довольно странно). 1. Динамически сгенерируйте класс Temp. 2. Получить базовый объект и использовать его в программе. 3. Позже потенциально сериализуйте базу, как и любой другой объект Base. 4. Возможно, перезагрузите сериализованный базовый объект в другом сеансе JVM, где Base находится в загрузчике классов, но Temp давно выброшен.   -  person richard    schedule 23.02.2013
comment
Ваша проблема может быть понята намного лучше, если вы опубликуете другую часть своего кода... при сериализации и десериализации объекта базового класса.   -  person Vishal K    schedule 23.02.2013
comment
@VishalK, Вишал, мой комментарий мог показаться снисходительным, хотя я этого не хотел, извиняюсь. Я не решаюсь публиковать больше кода, так как программа включает в себя десятки классов, и трудно публиковать что-либо еще без контекста.   -  person richard    schedule 23.02.2013


Ответы (2)


Попробуйте реализовать writeReplace в вашем объекте Temp, чтобы он записал Base, и реализовать readResolve в вашем объекте Base, чтобы он создал Temp, который его обертывает. См. Сериализуемый контракт.

Код будет выглядеть примерно так. Обратите внимание, что я на самом деле не проверял это, но это должно быть довольно близко.

public class Base implements Serializable {
    // Existing members here

    private Object readResolve() throws ObjectStreamException {
        return new Temp(this);
    }
}

public class Temp implements Serializable {
    Base base;

    public Temp(Base base) {
        this.base = base;
    }

    // Other existing methods here

    private Object writeReplace() throws ObjectStreamException {
        return base;
    }
}

Я понимаю, что это вводит неприятную круговую зависимость между этими двумя классами, но я не вижу другого отличного способа сделать это, кроме внешнего метода чтения/записи, как предлагали другие. Преимущество этого подхода в том, что он поддерживает запись Temp как части более крупного графа объектов.

person Eric Galluzzo    schedule 23.02.2013
comment
Привет, Эрик Галлуццо. Мне трудно понять, как реализовать то, что вы предложили (немного выше моей зарплаты скромного экономиста). Мне было интересно, не будет ли слишком большой проблемой, если вы могли бы быть немного более конкретным в том, что вы имели в виду, - person richard; 24.02.2013

Ну, во-первых, Ричард, это действительно первый раз, когда я вижу этот необычный случай, спасибо, что рассказали о нем ;) Здесь я разработал способ решить вашу проблему. Я надеюсь, что это может быть полезно для вас.:

import java.io.*;
abstract class Base implements Serializable
{
    protected int x ;
    private static final long serialVersionUID = 1L;
    public Base(int x) { this.x = x; }
    public abstract void baseMethod();
}
class Temp implements Serializable
{
    private static final long serialVersionUID = 2L;//Make Temp serializable also.
    public Base getBase(int i) 
    {
        Base base = new Base(i)
        {
            static final long serialVersionUID = 1L;
            @Override
            public void baseMethod() { System.out.println(x); } 
        };
        return base;
    }
}
public class ReadWriteBaseObject 
{
    public static Base createBaseObject(int i)
    {
        Temp temp = new Temp();//You might be creating your Temp object here Dynamically..
        Base base = temp.getBase(i);
        return base;
    }
    public static Base read()
    {
        ObjectInputStream oin = null;
        try
        {
            FileInputStream fin = new FileInputStream("BASE.ser");
            oin = new ObjectInputStream(fin);
            Base base = (Base)oin.readObject();
            return base;
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
            return null;
        }
        finally
        {
            if (oin!=null)
            {
                try
                {
                    oin.close();
                }
                catch (Exception ex){}
            }
        }
    }
    public static void write(Base base)
    {
        if (base == null)
        {
            System.out.println("Can't write null");
            return;
        }
        ObjectOutputStream os = null;
        try
        {
            FileOutputStream fos = new FileOutputStream("BASE.ser");
            os = new ObjectOutputStream(fos);
            os.writeObject(base);
            System.out.println("Wrote to file");
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
        finally
        {
            if (os!=null)
            {
                try
                {
                    os.close();
                }
                catch (Exception ex){}
            }
        }
    }
    public static void main(String st[])
    {
        Base base = createBaseObject(45);
        write(base);
        Base obj = read();
        obj.baseMethod();
    }
}

EDIT
Хотя экземпляры анонимных классов могут быть успешно сериализованы, Java настоятельно не рекомендует это из-за некоторых сложностей. Здесь это то, что официальный сайт Java состояния:

Примечание. Сериализация внутренних классов (т. е. вложенных классов, которые не являются статическими классами-членами), включая локальные и анонимные классы, настоятельно не рекомендуется по нескольким причинам. Поскольку внутренние классы, объявленные в нестатических контекстах, содержат неявные непереходные ссылки на вмещающие экземпляры классов, сериализация такого экземпляра внутреннего класса также приведет к сериализации связанного с ним экземпляра внешнего класса. Синтетические поля, сгенерированные javac (или другими компиляторами JavaTM) для реализации внутренних классов, зависят от реализации и могут различаться между компиляторами; различия в таких полях могут нарушить совместимость, а также привести к конфликту
значений serialVersionUID по умолчанию. Имена, присвоенные локальным и анонимным внутренним классам, также зависят от реализации и могут отличаться в разных компиляторах. Поскольку внутренние классы не могут объявлять статические члены, отличные от постоянных полей времени компиляции, они не могут использовать механизм serialPersistentFields для обозначения сериализуемых полей. Наконец, поскольку внутренние классы, связанные с внешними экземплярами, не имеют конструкторов с нулевым аргументом (конструкторы таких внутренних классов неявно принимают охватывающий экземпляр в качестве предшествующего параметра), они не могут реализовать Externalizable. Однако ни одна из перечисленных выше проблем не относится к статическим классам-членам.

Чтобы узнать больше о сериализации в Java, посмотрите < сильно>здесь

person Vishal K    schedule 23.02.2013
comment
Спасибо, ВишалК. Поправьте меня, если я ошибаюсь, но разве это не потребует, чтобы Temp был в пути к классам в более поздних сеансах, когда я пытаюсь десериализовать? - person richard; 23.02.2013
comment
@richard, ты не можешь использовать конкретный класс Base? - person Vishal K; 24.02.2013
comment
Что ты имеешь в виду? По определенным причинам Base должен оставаться и абстрактным классом. - person richard; 24.02.2013
comment
@richard Я понял, что нет смысла сериализовать абстрактный класс (до тех пор, пока он не будет содержать некоторое состояние), поскольку они не могут быть созданы. Здесь мы делаем сериализацию анонимной реализации типа абстрактного класса, определенного во внешнем классе Temp. Таким образом, base подобен члену класса Temp, заключенному в объект Temp. Итак, мы не можем десериализовать Base, не десериализовав сначала Temp. - person Vishal K; 25.02.2013
comment
@richard: смотрите мое обновление, чтобы узнать, почему Java не рекомендует сериализацию экземпляров анонимных классов. - person Vishal K; 25.02.2013