Reputation: 1509
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?
Upvotes: 67
Views: 93633
Reputation: 1
I had HIGH Warning for a protected field in a serializable class. Add transient for the field resolved my problem:
protected transient Object objectName;
Upvotes: 0
Reputation: 4797
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).
Upvotes: -1
Reputation: 14675
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);
}
}
Upvotes: 2
Reputation: 346476
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.
Serializable
. To do that, create an interface SerializableSet extends Set, Serializable
and use it for your field. Then, either:
SerializableSet
in the public interface and provide implementation classes that implement it. instanceof Serializable
and if they're not, copy them into something that is.Upvotes: 34
Reputation: 154090
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();
}
Upvotes: 20
Reputation: 504
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
Upvotes: 7
Reputation: 2850
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.
Upvotes: 9
Reputation: 192
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.
Upvotes: 14