Вопрос

Метод ObjectOutputStream.writeStreamHeader() может быть переопределен для добавления или добавления данных в заголовок.Однако, если эти данные основаны на аргументе, переданном конструктору производного класса, например:

public class MyObjectOutputStream extends ObjectOutputStream {

    public MyObjectOutputStream( int myData, OutputStream out ) throws IOException {
        super( out );
        m_myData = myData;
    }

    protected void writeStreamHeader() throws IOException {
        write( m_myData );            // WRONG: m_myData not initialized yet
        super.writeStreamHeader();
    }

    private final int m_myData;
}

это не работает, потому что super() вызывается раньше m_myData инициализируется и super() звонки writeStreamHeader().Единственный способ, которым я могу обойти это, - использовать ThreadLocal нравиться:

public class MyObjectOutputStream extends ObjectOutputStream {

    public MyObjectOutputStream( int myData, OutputStream out ) throws IOException {
        super( thunk( myData, out ) );
    }

    protected void writeStreamHeader() throws IOException {
        write( m_myData.get().intValue() );
        super.writeStreamHeader();
    }

    private static OutputStream thunk( int myData, OutputStream out ) {
        m_myData.set( myData );
        return out;
    }

    private static final ThreadLocal<Integer> m_myData = new ThreadLocal<Integer>();
}

Кажется, это работает, но есть ли лучший (менее неуклюжий) способ?

Это было полезно?

Решение

Существует общий способ решения такого рода проблем.Создайте класс и внутренний класс и укажите переменную во внешней области.(Обратите внимание, это работает только с -target 1.4 или greter, который используется по умолчанию в текущих версиях javac.С -target 1.3 вы получите NPE.)

public static ObjectOutputStream newInstance(
    final int myData, final OutputStream out
) throws IOException {
    return new ObjectOutputStream(out) {
        @Override
        protected void writeStreamHeader() throws IOException {
            write(myData);
            super.writeStreamHeader();
        }
    };
}

Но, вероятно, проще просто записать данные, прежде чем создавать ObjectOuputStream.

Другие советы

Не могли бы вы сделать это вот так?Игнорируйте вызов writeStreamHeader из суперконструктора и сделайте его самостоятельно, когда вы инициализировали необходимое поле:

public class MyObjectOutputStream extends ObjectOutputStream {

private boolean initalized = false;
private final int m_myData;

protected MyObjectOutputStream(int myData, OutputStream out) throws IOException, SecurityException {
    super(out);
    m_myData = myData;
    initalized = true;
    writeStreamHeader();
}

protected void writeStreamHeader() throws IOException {

    if(!initalized){
        return;
    }

    write( m_myData );
    super.writeStreamHeader();
}
}

РЕДАКТИРОВАТЬ:

Или, как предлагает Тило, это можно было бы записать так:

public class MyObjectOutputStream extends ObjectOutputStream {

    private final int m_myData;

    protected MyObjectOutputStream(int myData, OutputStream out) throws IOException, SecurityException {
        super(out);
        m_myData = myData;
        write( m_myData );
        super.writeStreamHeader();
    }

    protected void writeStreamHeader() throws IOException {
        // work is done in the constructor
    }
}

Как правило, вызывать нефинальные методы из конструктора — плохая идея (именно по той причине, которую вы указали).

Можете ли вы добиться собственной сериализации без расширения ObjectOutputStream?Я думаю о композиции потока.Например, вы можете добавить заголовок, записав его в базовый OutputStream до того, как это сделает ObjectOutputStream.Очевидно, что это невозможно сделать в подклассе ObjectOutputStream, но это можно легко сделать снаружи.

   out.write(myExtraHeader);
   ObjectOutputStream oos = new ObjectOutputStream(out);

Если хотите, вы можете красиво обернуть все это за интерфейсом ObjectOutput, как предложил Стю Томпсон в своем ответе, чтобы снаружи оно выглядело почти как ObjectOutputStream.

Обновлять:

Глядя на JavaDocs и исходный код ObjectOutputStream, есть второй (защищенный) конструктор, который не вызывает writeStreamHeader().

Однако этот конструктор также не инициализирует другие внутренние структуры.Чтобы процитировать документы, он предназначен для подклассов, которые полностью переопределяют ObjectOutputStream, чтобы не выделять частные данные, которые просто используются этой реализацией ObjectOutputStream ».В этом случае он также вызывает некоторые другие методы, например «writeObjectOverride».Неряшливый...

Используйте композицию вместо наследования.

public class MyObjOutStream implements DataOutput, ObjectOutput, ObjectStreamConstants {
    //same interfaces that ObjectOutputStream implements

    private ObjectOutputStream objOutStream;

    //implement all the methods below
}

Создайте экземпляр ObjectOutputStream только тогда, когда вы готовы это сделать.Остальные методы, которые вам необходимо реализовать в интерфейсах, могут просто вызывать те же методы в objOutStream

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top