Search code examples
javaserializationarraylistsonarqube

Java SonarQube rule squid:S1948 - how to use serializable lists without being bound to e.g. ArrayList


The SonarQube rule squid:S1948 requires that all fields of a serializable class are either serializable or transient. Another rule states to rather use the interface than the implementation types.

When I have a

public class myClass implements Serializable
{
  List<String> names;
  ...
}

then names should rather be a List<String> than e.g. ArrayList<String>. But then rule S1948 tells us that List isn't serializable.

Solution at a first glance: define an interface SerializableList - maybe like this:

public interface SerializableList<E> extends List<E>, Serializable
{
}

If we declare names to be a

  SerializableList<String> names;

the warning from rule S1948 is gone, but an assignment names = new ArrayList<String>(); results in a Type mismatch: cannot convert from ArrayList<String> to SerializableList<String>.

Two questions:

  • why can I declare List<String> names = new ArrayList<String>(); without a warning nor need to cast, but cannot declare SerializableList<String> names = new ArrayList<String>();? I wonder, because ArrayList<E> implements List<E>, Serializable, ... - why isn't it a SerializableList?
  • how to declare names in a way that neither S1948 nor the other rule will give a warning, but I can still be free to use any kind of List that is also Serializable (like ArrayList, ...) and without need for an explicit cast.

Thanks for any hints.


Solution

  • why can I declare List<String> names = new ArrayList<String>(); without a warning nor need to cast, but cannot declare SerializableList<String> names = new ArrayList<String>();? I wonder, because ArrayList<E> implements List<E>, Serializable, ... - why isn't it a SerializableList?

    You want to use a structure/feature which does not exist in Java ("a grouping interface"). Right now you introduced a new type SerializableList which extends other types:

    List    Serializable
      ↑          ↑
    SerializableList
    

    ArrayList also extends both parent types of the SerializableList:

    List   Serializable
      ↑         ↑
       ArrayList
    

    but they are no connection between SerializableList and ArrayList. If we define a new type:

    public class SerializableListImpl extends ArrayList implements SerializableList
    

    then we have:

       --------→ List ←---------
       |                       |
       |  --→ Serializable ←-- |
       | |                   | |
    ArrayList          SerializableList
          ↑                 ↑
          SerializableListImpl 
    

    and finally:

    List serializableList = new SerializableListImpl();
    System.out.println(serializableList instanceof List); // true
    System.out.println(serializableList instanceof Serializable); // true
    System.out.println(serializableList instanceof ArrayList); // true
    System.out.println(serializableList instanceof SerializableList); // true
    
    List arrayList = new ArrayList();
    System.out.println(arrayList instanceof List); // true
    System.out.println(arrayList instanceof Serializable); // true
    System.out.println(arrayList instanceof ArrayList); // true
    System.out.println(arrayList instanceof SerializableList); // false ← not in the hierarchy
    

    how to declare names in a way that neither S1948 nor the other rule will give a warning, but I can still be free to use any kind of List that is also Serializable (like ArrayList, ...) and without need for an explicit cast.

    You cannot. Not every implementation of the List interface have to be also Serializable. Anyone can define a new type:

    public class MyList implements List {
        ...
    }
    

    assign to the names field and then:

    List myList = new MyList();
    System.out.println(serializableList instanceof List); // true
    System.out.println(serializableList instanceof Serializable); // false ← not in the hierarchy
    

    As I wrote at the top such feature as "a grouping interface":

    SerializableList == List<String>&&Serializable
    

    does not exist so you have to mark the issue as Won't Fix.