Question

La méthode ObjectOutputStream.writeStreamHeader() peut être substituée à préfixer ou ajouter des données à l'en-tête. Toutefois, si ces données est basée sur un argument transmis au constructeur de la classe dérivée comme:

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;
}

il ne fonctionne pas parce que super() est appelée avant m_myData est initialisé et super() appelle writeStreamHeader(). La seule façon que je peux penser à contourner c'est en utilisant ThreadLocal comme:

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>();
}

Cela semble fonctionner, mais est-il un moyen de mieux (moins maladroits)?

Était-ce utile?

La solution

Il y a une façon générale pour résoudre ce genre de problème. Faire la classe et la classe intérieure et référence à une variable dans le champ extérieur. (Note, cela ne fonctionne qu'avec -target 1.4 ou Greter, qui est la valeur par défaut dans les versions actuelles de javac. Avec -target 1.3 vous obtiendrez un 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();
        }
    };
}

Mais, il est probablement plus facile de écrire les données avant la construction du ObjectOuputStream.

Autres conseils

Vous ne pourriez pas le faire comme ça. Ignorer l'appel writeStreamHeader du constructeur super et faire un vous-même, lorsque vous avez initialisé le champ nécessaire:

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();
}
}

EDIT:

Ou, comme suggéré par Thilo , il pourrait être écrit comme:

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
    }
}

Il est généralement une mauvaise idée d'appeler des méthodes non-finales du constructeur (pour la raison que vous avez présenté exactement).

Pouvez-vous atteindre votre sérialisation personnalisé sans extension ObjectOutputStream? Je pense à la composition du flux. Par exemple, vous pouvez préfixer votre tête en écrivant à l'OutputStream sous-jacente avant ObjectOutputStream ne. Cela ne peut évidemment pas être fait dans une sous-classe de ObjectOutputStream, mais il peut facilement être fait à partir de l'extérieur.

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

Si vous voulez, vous pouvez envelopper tout cela gentiment derrière l'interface ObjectOutput comme Stu Thompson a suggéré dans sa réponse, afin qu'il puisse se tourner vers l'extérieur presque comme un ObjectOutputStream.

Mise à jour:

En regardant les JavaDocs et la source pour ObjectOutputStream, il y a un second constructeur (protégé), qui ne remet pas writeStreamHeader().

Cependant, ce constructeur n'initialise pas aussi d'autres structures internes. Pour citer les documents, il est prévu « pour les sous-classes qui sont Réimplémenter complètement ObjectOutputStream de ne pas avoir à affecter les données privées juste utilisées par cette mise en œuvre de ObjectOutputStream ». Dans ce cas, il appelle aussi d'autres méthodes, telles que « writeObjectOverride ». Messy ...

composition d'utilisation à la place de l'héritage.

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

    private ObjectOutputStream objOutStream;

    //implement all the methods below
}

instance ObjectOutputStream que lorsque vous êtes prêt à le faire. Les autres méthodes que vous devez mettre en œuvre dans les interfaces peuvent simplement appeler les mêmes méthodes sur objOutStream

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top