Search code examples
javaoopserializationsingleton

JAVA - Implementing a serialisable and mutable Singleton


I am currently developing an Object-Oriented system in java. I am required to assign a unique integer identifier for every object within the application, and to be able to serialise and deserialise the entire contents of the application. I was pointed to the Singleton design pattern as a means of storing the java equivalent of "global variables", but the common implementations in java using a Class or Enum are unserialisable or immutable respectively. How would I go about making a singleton that is both mutable and serialisable?

Below is my current java code for a Singleton class I have written which should be non-serialisable since idCountSingleton is static:

import java.io.Serializable;


public class idCountSingleton implements Serializable{

    private static idCountSingleton INSTANCE;
    private int count;

    private idCountSingleton(int id){

        idCountSingleton INSTANCE = new idCountSingleton(id);
        count = id + 1;

    }

    public int getID(){
        return this.count;
    }

    public idCountSingleton getInstance(){
        return this.INSTANCE;
    }
}


Solution

  • The tricky part is to get INSTANCE assigned correctly on deserialisation.

    If you deserialise and then simply call getInstance you'll get a fresh instance of your singleton and lose the value of id.

    You can set INSTANCE by adding a side-effect to readObject which sets the static field:

    
    package com.example.so;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serial;
    import java.io.Serializable;
    
    public class SerializableSingleton implements Serializable {
        private static SerializableSingleton INSTANCE;
    
        private long id;
    
        public static synchronized SerializableSingleton getInstance() {
            if (INSTANCE == null) {
                System.out.println("Creating new SerializableSingleton");
                INSTANCE = new SerializableSingleton();
            }
            return INSTANCE;
        }
    
        long getAndIncrementId() {
            return id++;
        }
    
        @Serial
        private synchronized void readObject(java.io.ObjectInputStream s)
                throws IOException, ClassNotFoundException {
            s.defaultReadObject();
            INSTANCE = this;
        }
    
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            if (new File("test").exists()) {
                try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test"))) {
                    // read and discard the object
                    ois.readObject();
                    System.out.println("Read singleton, next id:" + SerializableSingleton.getInstance().getAndIncrementId());
                }
            }
            try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test"))) {
                oos.writeObject(SerializableSingleton.getInstance());
            }
    
        }
    }
    
    

    I have changed your class to increment the id when you get an id from an instance, instead of when getInstance is called, so that you can't accidentally get the same id twice.