【问题标题】:Double dispatch in Java exampleJava示例中的双重调度
【发布时间】:2012-10-31 05:00:22
【问题描述】:

我正在阅读关于DD 的维基百科文章并跳转到最后给出的"Double dispatch in Java and an example" 链接。以下 Serializable 示例的描述对我来说似乎相当混乱:

A a = new A();
ObjectOutputStream oos = new ObjectOutputStream();
oos.writeObject( a);

这里是描述:

为了序列化A,ObjectOutputStream首先查看方法writeObject( ObjectOutputStream oos)是否存在。如果是这样,那么它以自身作为参数调用该方法。然后writeObject 方法将调用分派回ObjectOutputStream(从而使其成为双重分派)。在进行这个非常简单的回调时,ObjectOutputStream 说:“我将把你的状态写入这个流的责任委托给你”。在做出这个简单的选择时,ObjectOutputStream 已经与我们的对象A 分离。对象A 反过来说好的,这是我想写在流上的一些状态。如果该值是一个原始值,那么它可以通过 write 方法的重载来处理。如果没有,则可以在对象图中的下一层继续来回进行,直到所有有用的状态都放在流上。

我猜测描述可能会令人困惑,因为描述的是幕后发生的事情,而不是呈现的代码,否则它似乎没有多大意义。以下是让我感到困惑的部分:

  • ObjectOutputStream 首先查看方法writeObject( ObjectOutputStream oos) 是否存在”。为什么ObjectOutputStream 需要检查这个方法是否存在,因为它是它自己的方法?
  • “如果确实如此,那么它会以自身作为参数调用该方法”。在我看来,它使用A 的实例作为参数调用writeObject。回到上一项,如果使用A 的实例调用它,为什么要查找带有ObjectOutputStream arg 的writeObject 签名?
  • “然后writeObject 方法将调用分派回ObjectOutputStream(因此使其成为双重分派)”。同样,writeObject 方法属于 ObjectOutputStream 类,所以我看不到它是如何“派回ObjectOutputStream”的,因为那似乎是原始目的地。

我只是在这里遗漏了一些基本的东西,还是这是一个写得不好/描述不好的例子?如果这是一个不好的例子,我想将 Wikipedia 文章更改为指向更好的文章,因此请随时提供建议。

谢谢。

【问题讨论】:

  • 感谢您的精彩回答。我仍然相信,如果对底层机制有一个简短的描述/解释,这篇文章会更好。
  • 技术上准确,但措辞笨拙。你说得对,一两个例子会有所帮助。

标签: java double-dispatch


【解决方案1】:

按顺序回答您的问题:

  1. ObjectOutputStream.writeObject(Object) 检查其参数(使用反射)以确定对象是否实现writeObject(ObjectOutputStream)。也就是说,它或多或少地询问对象,“你知道如何将你的结构写入ObjectOutputStream吗?”

  2. 如果答案是“是”,则ObjectOutputStream 调用对象的 writeObject(ObjectOutputStream) 方法,将自身(ObjectOutputStream,而不是对象)作为参数传递。它基本上说,“好。把你的结构写给我。”

  3. 对象中writeObject(ObjectOutputStream) 的实现调用ObjectOutputStream 来写入自身的元素。它不应该再次调用oos.writeObject(this)

例如,假设我有一个这样的对象:

class A {
    int x;
}

如果我想让它自己写入ObjectOutputStream,我可以像这样扩展它:

class A implements Serializable {
    int x;
    private void writeObject(ObjectOutputStream stream) throws IOException {
        stream.writeInt(x);
    }
}

然后这段代码:

A a = new A();
ObjectOutputStream oos = . . .;
oos.writeObject(a);

会使用A类中的writeObject方法来写a,而不是使用反射来写A的所有(非瞬态)字段。(但是,在这种情况下,没有区别.)

【讨论】:

  • 这就解释了。谢谢。
【解决方案2】:

来自Serializable接口的文档:

在序列化过程中需要特殊处理的类和 反序列化过程必须使用这些实现特殊方法 确切的签名:

private void writeObject(java.io.ObjectOutputStream out) 抛出 IOException private void readObject(java.io.ObjectInputStream in) 抛出 IOException,ClassNotFoundException;私人无效readObjectNoData() 抛出 ObjectStreamException;

如果你想让你的类可序列化,实现接口就足够了。但是,如果你想让(类的)对象本身告诉输出流如何序列化它自己的状态,那么它应该实现这些方法。

【讨论】:

  • 啊,是的。我应该有 RTFM(我通常这样做)。谢谢。
猜你喜欢
  • 2023-03-10
  • 2015-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多