we can see this problem in the return value of List<Player> . What is actually returned will
be a copy of the objects that reside in the StatRecorder address space. But the actual type of
the overall list will be a subtype of List (if it comes from an instance of our StatRecorder-
Impl class, it will be a LinkedList ), and the objects stored in the list will be a subtype of the
type Player (again, if it comes from an instance of our StatRecorderImpl class, it will be a
PlayerImpl ). How do we make sure that the correct classes are used to reconstruct the copy?
For that matter, what is the right class to use?
The Network Objects system was built at a time when good performance required your code
to be compiled into machine-specific binaries. Because of this, when an object was passed by
value from one address space to another, there was no guarantee that all of the classes avail-
able on the sending machine were available on the receiving machine. Although the data could
be sent from one machine to another, reconstructing an object requires not just the state but
the code that is used to manipulate that state. The solution taken by the authors of the Net-
works Objects system was dependent on the fact that the Modula-3 language had only a single
inheritance type system. So when an object was passed by value, it was reconstructed as the
closest possible type residing on the receiving machine. If the actual type was available, the
object would be reconstructed to its actual type. But if the actual type was not available, the
reconstruction would walk up the type tree (which would be a line) until it found the first class
that was resident on the local machine, and reconstruct the object as an instance of that type.
This approach was the best that could be done given the requirements of the system, but did
lead to some complicating outcomes. The worst of these is that when passing an object by
value you could never know exactly how that object would be reconstructed by the receiver
unless you knew the classes that were installed on the receiver. This wasn't a problem for sys-
tem classes, which could be assumed to be available everywhere. But for user-defined objects,
this meant that you could not be assured that you would get the same answer to otherwise
identical calls to different machines.
Like Network Objects, RMI was designed to be an RPC system that assumed both ends of
passing objects by value as well as by reference from one address space to another. Unlike
Network Objects, however, Java compiles to bytecodes that can be run on any Java virtual
machine, and the JVM allows classes to be dynamically loaded. So RMI was able to solve the
problem of passing subtypes by value in a different way.
When RMI passes a subtype by value, what gets reconstructed on the receiving side is exactly
the same type as the original object. The serialized stream that is sent from one address space
to the other is annotated with the actual class of the original object and a location on the net-