How to handle a Findbugs "Non-transient non-serializable instance field in serializable class"?
JavaSerializationFindbugsJava Problem Overview
Consider the class below. If I run Findbugs against it it will give me an error ("Non-transient non-serializable instance field in serializable class") on line 5 but not on line 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 }
That's correct because java.util.Set never implements Serializable in its hierarchy and java.util.HashSet does. However it is best practice to code against interfaces instead of concrete implementations.
How can I best handle this?
I can add a @Suppresswarnings(justification="No bug", values="SE_BAD_FIELD") on line 3. I have quite a lot of Sets and Lists in my actual code and I'm afraid it will litter my code too much.
Are there better ways?
Java Solutions
Solution 1 - Java
> 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 interfaceSerializableSet 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 - Java
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 - Java
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 - Java
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 - Java
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>
Solution 6 - Java
Use a concrete Serializable set for your internal representation, but make any public interfaces use the Set interface.
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);
}
}
Solution 7 - Java
I had HIGH Warning for a protected field in a serializable class. Add transient for the field resolved my problem:
protected transient Object objectName;
Solution 8 - Java
In case you are using findbugs-maven-plugin and have to persist a field, and that field is a class not implementing Serializable interface, for example, a field that has a class defined in a 3rd party. You can manually configure exclude file for findbugs,
If this is the only case, add it in an exclude file: pom:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<version>3.0.3</version>
<configuration>
<xmlOutput>true</xmlOutput>
<xmlOutputDirectory>target/findbugs/</xmlOutputDirectory>
<excludeFilterFile>findbugs-exclude.xml</excludeFilterFile>
<includeFilterFile>findbugs-include.xml</includeFilterFile>
<failOnError>true</failOnError>
</configuration>
...
exclude.xml:
<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter>
<Match>
<Class name="com.xxx.Foo" />
<Field type="org.springframework.statemachine.StateMachineContext"/>
</Match>
Entity:
@Entity
public class Foo extends Boo {
StateMachineContext<A, B> stateMachineContext;
Although I don't understand why adding <Bug category="SE_BAD_FIELD"/>
would not work. Besides, I don't agree with the solution of adding annotation on the field like @edu.umd.cs.findbugs.annotations.SuppressWarnings(justification="No bug", values="SE_BAD_FIELD")
, because building tools better not penetrate business code.maven plugin usage & findbugs filters both include and exclude
About SE_BAD_FIELD: Non-transient non-serializable instance field in serializable class, I think it should not check on entities. Because, javax.persistence.AttributeConverter
offers methods to serialize a field out side (implements Serializable is an inside method to serialize).