Java 将最终实例变量外部化

Java 将最终实例变量外部化,java,serialization,deserialization,serializable,externalizable,Java,Serialization,Deserialization,Serializable,Externalizable,以下是我的示例代码: public class ExternalizableClass implements Externalizable { final int id; public ExternalizableClass() { id = 0; } public ExternalizableClass(int i) { id = i; } @Override public void writeExternal(ObjectOutput

以下是我的示例代码:

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;
  }
}
它无法编译,因为
id=in.readInt()给出
错误:(36,5)java:无法将值分配给最终变量id
。然而,我可以想到真实的用例,其中一个不可变的字段,比如id,需要外部化,同时我们也希望保持其不可变性


那么,解决这个问题的正确方法是什么呢?

读取函数对于最终字段的概念没有多大意义,因为它初始化为的任何值都应该永远是它的值。read函数不应该能够更改它

显然,用
public ExternalizableClass(int i)
构造函数初始化的对象不应该能够读取新值——如果可以,那么它们的
id
值就不是最终值。我能看到的唯一其他方法是使默认构造函数初始化一个“未读”实例,允许您稍后调用read。但是,这确实需要删除最后一个修改器并解决该问题。所以看起来是这样的:

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";
  }
}

readExternal()
是一个实例方法,应该返回
void
。啊,忘了它还必须符合接口方法头。我将删除该答案。您更新的答案似乎仍然不正确。您的答案假设除了外部化之外,不会使用默认构造函数。然而,在实际情况中,也可能使用默认构造函数,因此使用
initted
是有缺陷的。那么,您如何知道id字段何时才是最终的呢?我想您可以要求用户显式地为initted提供一个值。如果一个特定的值(比如id 0)是为一种特殊类型的实例保留的,而其他实例可以指定它们自己的id呢?您可以将该字段封装在一个类中,然后在读入数据时创建所述类的实例吗?除此之外,在构造实例后重新初始化最终实例变量是没有意义的;这就是
final
@VinceEmigh的全部要点,但在本例中,我不会重新初始化对象。这是外部化,所以我想将obj保存到文件中,并重新构造它以供以后使用。我知道,但是如果您确实需要字段为最终字段,那么如果您需要使用外部化初始化字段,则无法实现它。您可以在读取时实例化一个新对象,然后在写入时取消引用该对象。或者,您可以切换到
Serializable
:“我想您很难从使用现代JVM的外部化中获得有意义的好处。”-