Externalize a final instance variable

851 Views Asked by At

Here's my sample code:

public class ExternalizableClass implements Externalizable
{
  final int id;

  public ExternalizableClass()
  {
    id = 0;
  }

  public ExternalizableClass(int i)
  {
    id = i;
  }

  @Override
  public void writeExternal(ObjectOutput out) throws IOException
  {
    out.writeInt(id);
  }

  @Override
  public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
  {
    id = in.readInt();
  }

  @Override
  public String toString()
  {
    return "id: " + id;
  }
}

It fails to compile because id = in.readInt(); gives Error:(36, 5) java: cannot assign a value to final variable id. However, I can think of real use cases where an immutable field, such as id, needs to be externalized, while we also want to preserve its immutability.

So what's the correct way to resolve this issue?

1

There are 1 best solutions below

6
On

The read function doesn't make much sense with the idea of a final field, because whatever value it was initialized to should be its value, forever. The read function shouldn't be able to change it.

Clearly objects initialized with the public ExternalizableClass(int i) constructor shouldn't be able to read a new value - if they can then their id value isn't really final. The only other way I could see doing this is making the default constructor initialize an "unread" instance, allowing you to call read on it later. This does, however, require removing the final modifier and working around that. So that would look like this:

public class ExternalizableClass implements Externalizable
{
  private int id;
  private boolean initted;

  int getId(){
      return id;
  }

  public ExternalizableClass(int i, boolean initted){
      id = i;
      this.initted = initted;
  }

  public ExternalizableClass(){
      this(0, true); //Default instances can't be changed
  }

  public ExternalizableClass(int i)
  {
    this(i, true); //Instances from this constructor can't be changed either
  }

  @Override
  public void writeExternal(ObjectOutput out) throws RuntimeException, IOException
  {
    if(! initted)
        throw new RuntimeException("Can't write unitialized instance, " + this);
    out.writeInt(id);
  }

  @Override
  public void readExternal(ObjectInput in) throws RuntimeException, IOException, ClassNotFoundException
  {
    if(initted)
        throw new RuntimeException("Can't Read into already initialized object ," + this);
    id = in.readInt();
    initted = true;
  }

  @Override
  public String toString()
  {
    if(initted) return "id: " + id;
    else return "No id";
  }
}