How to handle a Findbugs "Non-transient non-serializable instance field in serializable class"?

Solution 1:

However it is best practice to code against interfaces instead of concrete implementations.

I submit that no, in this case it is not. Findbugs quite correctly tells you that you risk running into a NotSerializableException as soon as you have a non-serializable Set implementation in that field. This is something you should deal with. How, that depends on the design of your classes.

  • If those collections are initialized within the class and never set from outside, then I see absolutely nothing wrong with declaring the concrete type for the field, since fields are implementation details anyway. Do use the interface type in the public interface.
  • If the collection are passed into the class via a public interface, you have to ensure that they are in fact Serializable. To do that, create an interface SerializableSet extends Set, Serializable and use it for your field. Then, either:
    • Use SerializableSet in the public interface and provide implementation classes that implement it.
    • Check collections passed to the class via instanceof Serializable and if they're not, copy them into something that is.

Solution 2:

I know this is an old question that's already answered but just so others know is that you can set the Set<Integer> field as transient if you have no interest in serializing that particular field which will fix your FindBugs error.

public class TestClass implements Serializable {

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

}

I prefer this method instead of forcing users of your API to cast to your concrete type, unless it's just internal, then Michael Borgwardt's answer makes more sense.

Solution 3:

You can get rid of those Critical warning messages by adding the following methods to your class:

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

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

Solution 4:

You could use a capture helper to ensure that a passed in Set supports two 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);
    }
}

In this way you can have a public API that enforces Serializable without checking/requiring specific implementation details.

Solution 5:

I use a findbugs-exclude Filter for collection-Fields:

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

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