【问题标题】:Externalize a final instance variable外部化最终实例变量
【发布时间】:2015-02-24 04:20:39
【问题描述】:

这是我的示例代码:

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(); 给出了Error:(36, 5) java: cannot assign a value to final variable id。但是,我可以想到实际用例,其中不可变字段(例如 id)需要外部化,同时我们还希望保留其不变性。

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

【问题讨论】:

  • 您可以将该字段封装在一个类中,然后在读取数据时创建该类的实例吗?除此之外,在构造实例之后重新初始化最终实例变量确实没有意义;这就是final的重点
  • @VinceEmigh 但在这种情况下,我不会重新初始化对象。这是外化,所以我想将 obj 保存到文件并重新构建以供以后使用。
  • 我知道,但是如果您真的需要该字段是最终的,那么如果您需要使用外部化来初始化该字段,那么您将无法实现。您总是可以在读取时实例化一个新对象,然后在写入时取消引用该对象。或者,你可以切换到Serializable:“我怀疑你很难从使用现代 JVM 的 Externalizable 中获得有意义的好处。” - stackoverflow.com/a/818093/2398375

标签: java serialization deserialization serializable externalizable


【解决方案1】:

read 函数对于 final 字段的想法没有多大意义,因为无论它被初始化为什么值都应该是它的值,永远。读取函数应该不能改变它。

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

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 呢?
猜你喜欢
  • 2014-06-20
  • 2011-04-24
  • 2023-03-28
  • 1970-01-01
  • 2021-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多