Comment remplacer ObjectOutputStream.writeStreamHeader ()?
-
23-08-2019 - |
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)?
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