Double dispatch in Java example

5.6k views Asked by At

I was reading the Wikipedia article on DD and jumped over to the "Double dispatch in Java and an example" link given at the end. The description of the following Serializable example seems rather confusing to me:

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

Here's the description:

In order to serialize A, ObjectOutputStream first looks to see if the method writeObject( ObjectOutputStream oos) exists. If it does, then it calls that method with itself as an argument. The writeObject method then dispatches the call back to the ObjectOutputStream (thus making this a double dispatch). In making this very simple call back, ObjectOutputStream has said: "I'm delegating back to you the responsibility of writing your state onto this stream". In making that simple choice, ObjectOutputStream has decoupled itself from our object A. Object A in turn says ok, here is some state that I want written on the stream. If the value is a primitive, then it can be dealt by the overloading of the write method. If not, the back and forth can continue at the next level down in the objects graph until all of the useful state has been placed on the stream.

I'm guessing that the description might be confusing because what is being described is what is happening behind the scenes and not the code presented because otherwise it doesn't seem to make much sense. Here are the parts that confuse me:

  • "ObjectOutputStream first looks to see if the method writeObject( ObjectOutputStream oos) exists". Why would ObjectOutputStream need to check for the existence of this method since it is its own method?
  • "If it does, then it calls that method with itself as an argument". It looks to me like it's calling writeObject with an instance of A as an argument. Going back to the previous item, why would it look for the writeObject signature with an ObjectOutputStream arg if it's being called with an instance of A?
  • "The writeObject method then dispatches the call back to the ObjectOutputStream (thus making this a double dispatch)". Again, the writeObject method belongs to the ObjectOutputStream class so I fail to see how this gets "dispatched back to the ObjectOutputStream" since that appears to be the original destination.

Am I just missing something fundamental here or is this a poorly written/described example? If it is a bad example, I'd like to change the Wikipedia article to point to a better one so feel free to provide suggestions.

Thanks.

2

There are 2 answers

1
Ted Hopp On BEST ANSWER

To answer your questions in order:

  1. ObjectOutputStream.writeObject(Object) checks its argument (using reflection) to determine if the object implements writeObject(ObjectOutputStream). That is, it more or less asks the object, "Do you know how to write your structure to an ObjectOutputStream?"

  2. If the answer is "yes", then the ObjectOutputStream calls the object's writeObject(ObjectOutputStream) method, passing itself (the ObjectOutputStream, not the object) as the argument. It basically says, "Good. Write your structure to me."

  3. The implementation of writeObject(ObjectOutputStream) in the object calls on the ObjectOutputStream to write elements of itself. It is not supposed to call oos.writeObject(this) again.

For example, suppose I had an object like this:

class A {
    int x;
}

If I wanted to enable it to write itself to an ObjectOutputStream, I might expand it like this:

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

Then this code:

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

would use the writeObject method in class A to write a, rather than using reflection to write all the (non-transient) fields of A. (In this case, however, there would be no difference.)

1
Bhesh Gurung On

From the documentation of the Serializable interface:

Classes that require special handling during the serialization and deserialization process must implement special methods with these exact signatures:

private void writeObject(java.io.ObjectOutputStream out) throws IOException private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException; private void readObjectNoData() throws ObjectStreamException;

If you want to make your class serializable implementing the interface is enough. But, if you want make the object (of the class) itself tell the output stream how to serialize it's own state then it should implement those methods.