CodeQL documentation

Serializable inner class of non-serializable class

ID: java/non-serializable-inner-class
Kind: problem
Severity: warning
Precision: medium
Tags:
   - reliability
   - maintainability
   - language-features
Query suites:
   - java-security-and-quality.qls

Click to see the query in the CodeQL repository

Non-static nested classes that implement Serializable must be defined in an enclosing class that is also serializable. Non-static nested classes retain an implicit reference to an instance of their enclosing class. If the enclosing class is not serializable, the Java serialization mechanism fails with a java.io.NotSerializableException.

Recommendation

To avoid causing a NotSerializableException, do one of the following:

  • Declare the nested class as static : If the nested class does not use any of the non-static fields or methods of the enclosing class, it is best to declare it static. This removes the implicit reference to an instance of the enclosing class, and has the additional effect of breaking an unnecessary dependency between the two classes. A similar solution is to turn the nested class into a separate top-level class.
  • Make the enclosing class implement Serializable : However, this is not recommended because the implementation of inner classes may be compiler-specific, and serializing an inner class can result in non-portability across compilers. The Java Serialization Specification states:

    Serialization of inner classes (i.e., nested classes that are not static member classes), including local and anonymous classes, is strongly discouraged for several reasons. Because inner classes declared in non-static contexts contain implicit non-transient references to enclosing class instances, serializing such an inner class instance will result in serialization of its associated outer class instance as well. Synthetic fields generated by javac (or other Java(TM) compilers) to implement inner classes are implementation dependent and may vary between compilers; differences in such fields can disrupt compatibility as well as result in conflicting default serialVersionUID values. The names assigned to local and anonymous inner classes are also implementation dependent and may differ between compilers.

Example

In the following example, the class WrongSession cannot be serialized without causing a NotSerializableException, because it is enclosed by a non-serializable class. However, the class Session can be serialized because it is declared as static.

class NonSerializableServer {

    // BAD: The following class is serializable, but the enclosing class
    // 'NonSerializableServer' is not. Serializing an instance of 'WrongSession' 
    // causes a 'java.io.NotSerializableException'.
    class WrongSession implements Serializable {
        private static final long serialVersionUID = 8970783971992397218L;
        private int id;
        private String user;
        
        WrongSession(int id, String user) { /*...*/ }
    }
    
    public WrongSession getNewSession(String user) {
        return new WrongSession(newId(), user);
    }
}

class Server {

    // GOOD: The following class can be correctly serialized because it is static.
    static class Session implements Serializable {
        private static final long serialVersionUID = 1065454318648105638L;
        private int id;
        private String user;
        
        Session(int id, String user) { /*...*/ }
    }
    
    public Session getNewSession(String user) {
        return new Session(newId(), user);
    }
}

References