71 votes

champs transitoires finaux et sérialisation

Est-il possible d'avoir final transient les champs qui prennent une valeur autre que la valeur par défaut après la sérialisation en Java ? Mon cas d'utilisation est une variable de cache - c'est pourquoi il s'agit d'une valeur par défaut. transient . J'ai aussi l'habitude de faire Map les champs qui ne seront pas modifiés (c'est-à-dire que le contenu de la carte est modifié, mais l'objet lui-même reste le même) final . Cependant, ces attributs semblent être contradictoires - alors que le compilateur permet une telle combinaison, je ne peux pas faire en sorte que le champ soit défini sur autre chose que null après la désérialisation.

J'ai essayé ce qui suit, sans succès :

  • initialisation simple des champs (montrée dans l'exemple) : c'est ce que je fais normalement, mais l'initialisation ne semble pas se produire après la désérialisation ;
  • initialisation dans le constructeur (je crois que c'est sémantiquement la même chose que ci-dessus) ;
  • l'attribution du champ dans readObject() - ne peut pas être fait puisque le champ est final .

Dans l'exemple cache es public uniquement pour les tests.

import java.io.*;
import java.util.*;

public class test
{
    public static void main (String[] args) throws Exception
    {
        X  x = new X ();
        System.out.println (x + " " + x.cache);

        ByteArrayOutputStream  buffer = new ByteArrayOutputStream ();
        new ObjectOutputStream (buffer).writeObject (x);
        x = (X) new ObjectInputStream (new ByteArrayInputStream (buffer.toByteArray ())).readObject ();
        System.out.println (x + " " + x.cache);
    }

    public static class X implements Serializable
    {
        public final transient Map <Object, Object>  cache = new HashMap <Object, Object> ();
    }
}

Sortie :

test$X@1a46e30 {}
test$X@190d11 null

0voto

Liam Points 13

Cette question porte sur le sérialiseur par défaut de Java, mais j'ai atterri ici après avoir effectué une recherche sur Gson. Cette réponse ne s'applique pas au sérialiseur par défaut, mais elle s'applique à Gson et peut-être à d'autres. Je n'aimais pas trop utiliser (manuellement) Reflection ou readResolve alors voici quelque chose d'autre.

Lors de la désérialisation, Gson appelle le constructeur par défaut pour créer l'objet. Vous pouvez déplacer vos affectations finales transitoires vers le constructeur par défaut, et elles seront affectées correctement. Si vous n'avez qu'un constructeur non par défaut qui affecte des variables finales (par exemple, un ID), peu importe à quoi vous les affectez car elles seront écrasées par Gson avec Reflection.

Cela signifie que si vos affectations finales transitoires reposent sur les arguments des constructeurs, cela ne fonctionnera pas.

Voici un exemple de code :

import com.google.gson.Gson;
import java.util.HashMap;

public class Test {
    public static void main(String[] args) {

        BrokenTestObject broken = new BrokenTestObject("broken");
        FixedTestObject fixed = new FixedTestObject("fixed");

        broken = serializeAndDeserialize(broken, BrokenTestObject.class);
        fixed = serializeAndDeserialize(fixed, FixedTestObject.class);

        System.out.println(broken.id + ": " + broken.someCache);
        System.out.println(fixed.id + ": " + fixed.someCache);
    }

    public static <O> O serializeAndDeserialize(O object, Class<O> c) {
        Gson gson = new Gson();
        String json = gson.toJson(object);
        return gson.fromJson(json, c);
    }

    public static class BrokenTestObject {
        public final String id;
        public transient final HashMap<String, String> someCache = new HashMap<>();

        public BrokenTestObject(String id) {
            this.id = id;
        }
    }

    public static class FixedTestObject {
        public final String id;
        public transient final HashMap<String, String> someCache;

        public FixedTestObject(String id) {
            this.id = id;
            this.someCache = new HashMap<>();
        }

        //only used during deserialization
        private FixedTestObject() {
            this.id = null; //doesn't matter, will be overwritten during deserialization
            this.someCache = new HashMap<>();
        }
    }
}

Imprimés :

broken: null
fixed: {}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X