web-dev-qa-db-fra.com

Comment gérer un Findbugs "Champ d'instance non transitoire non sérialisable dans une classe sérialisable"?

Considérez la classe ci-dessous. Si j'exécute Findbugs contre cela, cela me donnera une erreur ("Champ d'instance non transitoire non sérialisable dans la classe sérialisable") sur la ligne 5 mais pas sur la ligne 7.

1 public class TestClass implements Serializable {
2
3  private static final long serialVersionUID = 1905162041950251407L;
4
5  private Set<Integer> mySet;      // Findbugs error
6
7  private HashSet<Integer> myOtherSet;
8
9 }

C'est correct car Java.util.Set n'implémente jamais Serializable dans sa hiérarchie et Java.util.HashSet le fait. Cependant, il est préférable de coder contre des interfaces au lieu d'implémentations concrètes.

Comment puis-je gérer au mieux cela?

Je peux ajouter un @Suppresswarnings (justification = "Pas de bug", values ​​= "SE_BAD_FIELD") sur la ligne 3. J'ai beaucoup d'ensembles et de listes dans mon code actuel et je crains que cela ne gâche trop mon code.

Y a-t-il de meilleures façons?

52
Koohoolinn

Cependant, il est préférable de coder contre des interfaces au lieu d'implémentations concrètes.

Je soutiens que non, dans ce cas, ce n'est pas le cas. Findbugs vous indique très correctement que vous risquez de rencontrer un NotSerializableException dès que vous avez une implémentation Set non sérialisable dans ce champ. C'est quelque chose que vous devez gérer. Comment, cela dépend de la conception de vos classes.

  • Si ces collections sont initialisées dans la classe et ne sont jamais définies de l'extérieur, je ne vois absolument rien de mal à déclarer le type concret pour le champ, car les champs sont de toute façon des détails d'implémentation. Utilisez le type d'interface dans l'interface publique.
  • Si la collection est passée dans la classe via une interface publique, vous devez vous assurer qu'elles sont bien Serializable. Pour ce faire, créez une interface SerializableSet extends Set, Serializable et utilisez-le pour votre domaine. Ensuite, soit:
    • Utilisez SerializableSet dans l'interface publique et fournissez les classes d'implémentation qui l'implémentent.
    • Vérifier les collections passées à la classe via instanceof Serializable et s'ils ne le sont pas, copiez-les dans quelque chose qui l'est.
28
Michael Borgwardt

Je sais que c'est une vieille question à laquelle on a déjà répondu, mais pour que les autres sachent, c'est que vous pouvez définir le Set<Integer> champ comme transitoire si vous n'avez aucun intérêt à sérialiser ce champ particulier qui corrigera votre erreur FindBugs.

public class TestClass implements Serializable {

    private static final long serialVersionUID = 1905162041950251407L;
    private transient Set<Integer> mySet;

}

Je préfère cette méthode au lieu de forcer les utilisateurs de votre API à couler sur votre type de béton, à moins qu'il ne soit juste interne, la réponse de Michael Borgwardt a plus de sens.

12
Graham

Vous pouvez vous débarrasser de ces messages d'avertissement Critical en ajoutant les méthodes suivantes à votre classe:

private void writeObject(ObjectOutputStream stream)
        throws IOException {
    stream.defaultWriteObject();
}

private void readObject(ObjectInputStream stream)
        throws IOException, ClassNotFoundException {
    stream.defaultReadObject();
}
9
Vlad Mihalcea

Vous pouvez utiliser un assistant de capture pour vous assurer qu'un ensemble transmis dans prend en charge deux interfaces:

private static class SerializableTestClass<T extends Set<?> & Serializable> implements Serializable
{
    private static final long serialVersionUID = 1L;
    private final T serializableSet;

    private SerializableTestClass(T serializableSet)
    {
        this.serializableSet = serializableSet;
    }
}

public static class PublicApiTestClass
{
    public static <T extends Set<?> & Serializable> Serializable forSerializableSet(T set)
    {
        return new SerializableTestClass<T>(set);
    }
}

De cette façon, vous pouvez avoir une API publique qui applique Serializable sans vérifier/nécessiter des détails d'implémentation spécifiques.

8
jontejj

J'utilise un filtre findbugs-exclude pour les champs de collection:

<Match>
    <Field type="Java.util.Map" />
    <Bug pattern="SE_BAD_FIELD" />
</Match>
<Match>
    <Field type="Java.util.Set" />
    <Bug pattern="SE_BAD_FIELD" />
</Match>
<Match>
    <Field type="Java.util.List" />
    <Bug pattern="SE_BAD_FIELD" />
</Match>

Voir http://findbugs.sourceforge.net/manual/filter.html

7
brabenetz

Utilisez un ensemble sérialisable concret pour votre représentation interne, mais faites en sorte que toutes les interfaces publiques utilisent l'interface Set.

public class TestClass implements Serializable {
    private static final long serialVersionUID = 1905162041950251407L;

    private HashSet<Integer> mySet;

    public TestClass(Set<Integer> s) {
        super();
        setMySet(s);
    }

    public void setMySet(Set<Integer> s) {
        mySet = (s == null) ? new HashSet<>() : new HashSet<>(s);
    }
}
1
Paul Croarkin