1

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?

OneZero
  • 11,556
  • 15
  • 55
  • 92
  • Could you possible encapsulate that field in a class, then create the instance of said class when you read in the data? Other than that, it really doesn't make sense to re-initialize a final instance variable after the instance has been constructed; that's the whole point of `final` – Vince Dec 27 '14 at 05:05
  • 1
    @VinceEmigh But in this case I'm not reinitializing the object. It's externalization so I want to save the obj to file and re-construct it for later use. – OneZero Dec 27 '14 at 19:24
  • I know, but if you really need the field to be final, you aren't gonna achieve it if you need to initialize the field using externalization. You could always instantiate a new object when you read, then dereference the object when you write. Or, you could switch to `Serializable`: "*I suspect you'd be hard-pressed to get a meaningful benefit from Externalizable with a modern JVM.*" - http://stackoverflow.com/a/818093/2398375 – Vince Dec 27 '14 at 19:34

1 Answers1

-1

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";
  }
}
Mshnik
  • 7,032
  • 1
  • 25
  • 38
  • `readExternal()` is an instance method, and should return `void`. – OneZero Dec 27 '14 at 04:02
  • Ah, forgot that it had to still conform to the interface method headers. I'll delete that answer. – Mshnik Dec 27 '14 at 04:04
  • Your updated answer still doesn't seem right. Your answer assumes that the default constructor won't be used except for externalization. However, in real situations the default constructor might also be used and therefore the use of `initted` is flawed. – OneZero Dec 27 '14 at 04:06
  • So then how do you know when the id field is actually final? I suppose you could require the user to explicitly provide a value for initted. – Mshnik Dec 27 '14 at 04:08
  • What if a particular value (say id 0) is reserved for a special type of instances while other instances can specify their own id's? – OneZero Dec 27 '14 at 04:09
  • Presumably the default constructor would set that value (otherwise what value would it be setting?), so you're basically doing what I had before. – Mshnik Dec 27 '14 at 04:11