- The ObjectInputStream Class
- The ObjectInputStream.GetField Class
- The ObjectInputValidation Interface
- The readObject Method
- The readExternal Method
- The readResolve Method
3.1 The ObjectInputStream Class
Class ObjectInputStream
implements object
deserialization. It maintains the state of the stream including the set
of objects already deserialized. Its methods allow primitive types and
objects to be read from a stream written by
ObjectOutputStream
. It manages restoration of the object
and the objects that it refers to from the stream.
package java.io;
public class ObjectInputStream
extends InputStream
implements ObjectInput, ObjectStreamConstants
{
public ObjectInputStream(InputStream in)
throws StreamCorruptedException, IOException;
public final Object readObject()
throws OptionalDataException, ClassNotFoundException,
IOException;
public Object readUnshared()
throws OptionalDataException, ClassNotFoundException,
IOException;
public void defaultReadObject()
throws IOException, ClassNotFoundException,
NotActiveException;
public GetField readFields()
throws IOException;
public void registerValidation(
ObjectInputValidation obj, int prio)
throws NotActiveException, InvalidObjectException;
protected ObjectStreamClass readClassDescriptor()
throws IOException, ClassNotFoundException;
protected Class<?> resolveClass(ObjectStreamClass v)
throws IOException, ClassNotFoundException;
protected Class<?> resolveProxyClass(String[] interfaces)
throws IOException, ClassNotFoundException;
protected Object resolveObject(Object obj)
throws IOException;
protected boolean enableResolveObject(boolean enable)
throws SecurityException;
protected void readStreamHeader()
throws IOException, StreamCorruptedException;
public int read() throws IOException;
public int read(byte[] data, int offset, int length)
throws IOException
public int available() throws IOException;
public void close() throws IOException;
public boolean readBoolean() throws IOException;
public byte readByte() throws IOException;
public int readUnsignedByte() throws IOException;
public short readShort() throws IOException;
public int readUnsignedShort() throws IOException;
public char readChar() throws IOException;
public int readInt() throws IOException;
public long readLong() throws IOException;
public float readFloat() throws IOException;
public double readDouble() throws IOException;
public void readFully(byte[] data) throws IOException;
public void readFully(byte[] data, int offset, int size)
throws IOException;
public int skipBytes(int len) throws IOException;
public String readLine() throws IOException;
public String readUTF() throws IOException;
// Class to provide access to serializable fields.
public abstract static class GetField
{
public abstract ObjectStreamClass getObjectStreamClass();
public abstract boolean defaulted(String name)
throws IOException, IllegalArgumentException;
public abstract char get(String name, char default)
throws IOException, IllegalArgumentException;
public abstract boolean get(String name, boolean default)
throws IOException, IllegalArgumentException;
public abstract byte get(String name, byte default)
throws IOException, IllegalArgumentException;
public abstract short get(String name, short default)
throws IOException, IllegalArgumentException;
public abstract int get(String name, int default)
throws IOException, IllegalArgumentException;
public abstract long get(String name, long default)
throws IOException, IllegalArgumentException;
public abstract float get(String name, float default)
throws IOException, IllegalArgumentException;
public abstract double get(String name, double default)
throws IOException, IllegalArgumentException;
public abstract Object get(String name, Object default)
throws IOException, IllegalArgumentException;
}
protected ObjectInputStream()
throws StreamCorruptedException, IOException;
protected readObjectOverride()
throws OptionalDataException, ClassNotFoundException,
IOException;
}
The single-argument ObjectInputStream
constructor
requires an InputStream
. The constructor calls
readStreamHeader
to read and verifies the header and
version written by the corresponding
ObjectOutputStream.writeStreamHeader
method. If a security
manager is installed, this constructor checks for the
"enableSubclassImplementation"
SerializablePermission
when invoked directly or indirectly
by the constructor of a subclass which overrides the
readFields
and/or readUnshared
methods.
Note: The ObjectInputStream
constructor
blocks until it completes reading the serialization stream header. Code
which waits for an ObjectInputStream
to be constructed
before creating the corresponding ObjectOutputStream
for
that stream will deadlock, since the ObjectInputStream
constructor will block until a header is written to the stream, and the
header will not be written to the stream until the
ObjectOutputStream
constructor executes. This problem can
be resolved by creating the ObjectOutputStream
before the
ObjectInputStream
, or otherwise removing the timing
dependency between completion of ObjectInputStream
construction and the creation of the
ObjectOutputStream
.
The readObject
method is used to deserialize an object
from the stream. It reads from the stream to reconstruct an object.
If the
ObjectInputStream
subclass is overriding the implementation, call thereadObjectOverride
method and return. Reimplementation is described at the end of this section.If a block data record occurs in the stream, throw a
BlockDataException
with the number of available bytes.If the object in the stream is null, return null.
If the object in the stream is a handle to a previous object, return the object.
If the object in the stream is a
Class
, read itsObjectStreamClass
descriptor, add it and its handle to the set of known objects, and return the correspondingClass
object.If the object in the stream is an
ObjectStreamClass
, read in its data according to the formats described in Section 4.3, "Serialized Form". Add it and its handle to the set of known objects. In versions 1.3 and later of the Java 2 SDK, Standard Edition, thereadClassDescriptor
method is called to read in theObjectStreamClass
if it represents a class that is not a dynamic proxy class, as indicated in the stream data. If the class descriptor represents a dynamic proxy class, call theresolveProxyClass
method on the stream to get the local class for the descriptor; otherwise, call theresolveClass
method on the stream to get the local class. If the class cannot be resolved, throw a ClassNotFoundException. Return the resultingObjectStreamClass
object.If the object in the stream is a
String
, read its length information followed by the contents of the string encoded in modified UTF-8. For details, refer to Section 6.2, "Stream Elements". Add theString
and its handle to the set of known objects, and proceed to Step 13.If the object in the stream is an array, read its
ObjectStreamClass
and the length of the array. Allocate the array, and add it and its handle in the set of known objects. Read each element using the appropriate method for its type and assign it to the array. Proceed to Step 13.If the object in the stream is an enum constant, read its
ObjectStreamClass
and the enum constant name. If theObjectStreamClass
represents a class that is not an enum type, anInvalidClassException
is thrown. Obtain a reference to the enum constant by calling thejava.lang.Enum.valueOf
method, passing the enum type bound to the receivedObjectStreamClass
along with the received name as arguments. If thevalueOf
method throws anIllegalArgumentException
, anInvalidObjectException
is thrown with theIllegalArgumentException
as its cause. Add the enum constant and its handle in the set of known objects, and proceed to Step 13.For all other objects, the
ObjectStreamClass
of the object is read from the stream. The local class for thatObjectStreamClass
is retrieved. The class must be serializable or externalizable, and must not be an enum type. If the class does not satisfy these criteria, anInvalidClassException
is thrown.If the class is a record class. A handle, with an initial value of
null
, is added to the set of known objects.The record object will be constructed by an invocation of its canonical constructor. The canonical constructor for record class,
R
, is found by, first building a method descriptor from the number, order, and declared types of the record R's components, as returned byR::getRecordComponents
; and then locating R's declared constructor matching the descriptor. If the canonical constructor cannot be found, anInvalidClassException
is thrown.The contents are restored as follows:
Read and restore all the field values from the stream. Match the stream fields to that of the appropriate constructor parameter, used to initialize the record component. The matching is based on the equality of the name of the stream field and the name of the record component, their names must be identical. That is, for each record component, as returned by
Class::getRecordComponents
, first determine the components's name,n
; and then find the stream value for the stream field whose name equalsn
. The concrete type of a stream value for a matched field, must be assignable (Class::isAssignableFrom
) to that of the type of its matched record component, or else aClassCastException
will be thrown -- all matches are type-checked. Stream fields not matched are effectively discarded.Invoke the record 's canonical constructor, with the matched stream field values. The stream field values are passed in the corresponding record component positions of the constructor parameters. Unmatched components have the default value appropriate for their type passed. If the constructor invocation throws an exception, an
InvalidObjectException
is thrown with that exception as its cause. Otherwise, the newly created record object is assigned to its handle of known objects. Proceed to Step 13.
An instance of the class is allocated. The instance and its handle are added to the set of known objects. The contents restored appropriately:
For serializable objects, the no-arg constructor for the first non-serializable supertype is run. For serializable classes, the fields are initialized to the default value appropriate for its type. Then the fields of each class are restored by calling class-specific
readObject
methods, or if these are not defined, by calling thedefaultReadObject
method. Note that field initializers and constructors are not executed for serializable classes during deserialization. In the normal case, the version of the class that wrote the stream will be the same as the class reading the stream. In this case, all of the supertypes of the object in the stream will match the supertypes in the currently-loaded class. If the version of the class that wrote the stream had different supertypes than the loaded class, theObjectInputStream
must be more careful about restoring or initializing the state of the differing classes. It must step through the classes, matching the available data in the stream with the classes of the object being restored. Data for classes that occur in the stream, but do not occur in the object, is discarded. For classes that occur in the object, but not in the stream, the class fields are set to default values by default serialization.For externalizable objects, the no-arg constructor for the class is run and then the
readExternal
method is called to restore the contents of the object.
Process potential substitutions by the class of the object and/or by a subclass of
ObjectInputStream
:If the class of the object is not an enum type and defines the appropriate
readResolve
method, the method is called to allow the object to replace itself.Then if previously enabled by
enableResolveObject,
theresolveObject
method is called to allow subclasses of the stream to examine and replace the object. If the previous step did replace the original object, theresolveObject
method is called with the replacement object. If a replacement took place, the table of known objects is updated so the replacement object is associated with the handle. The replacement object is then returned fromreadObject
.
All of the methods for reading primitives types only consume bytes
from the block data records in the stream. If a read for primitive data
occurs when the next item in the stream is an object, the read methods
return -1 or the EOFException
as appropriate. The
value of a primitive type is read by a DataInputStream
from
the block data record.
The exceptions thrown reflect errors during the traversal or exceptions that occur on the underlying stream. If any exception is thrown, the underlying stream is left in an unknown and unusable state.
When the reset token occurs in the stream, all of the state of the stream is discarded. The set of known objects is cleared.
When the exception token occurs in the stream, the exception is read
and a new WriteAbortedException
is thrown with the
terminating exception as an argument. The stream context is reset as
described earlier.
The readUnshared
method is used to read "unshared"
objects from the stream. This method is identical to
readObject
, except that it prevents subsequent calls to
readObject
and readUnshared
from returning
additional references to the deserialized instance returned by the
original call to readUnshared
. Specifically:
If
readUnshared
is called to deserialize a back-reference (the stream representation of an object which has been written previously to the stream), anObjectStreamException
will be thrown.If
readUnshared
returns successfully, then any subsequent attempts to deserialize back-references to the stream handle deserialized byreadUnshared
will cause anObjectStreamException
to be thrown.
Deserializing an object via readUnshared
invalidates the
stream handle associated with the returned object. Note that this in
itself does not always guarantee that the reference returned by
readUnshared
is unique; the deserialized object may define
a readResolve
method which returns an object visible to
other parties, or readUnshared
may return a
Class
object or enum constant obtainable elsewhere in the
stream or through external means. If the deserialized object defines a
readResolve
method and the invocation of that method
returns an array, then readUnshared
returns a shallow clone
of that array; this guarantees that the returned array object is unique
and cannot be obtained a second time from an invocation of
readObject
or readUnshared
on the
ObjectInputStream
, even if the underlying data stream has
been manipulated.
The defaultReadObject
method is used to read the fields
and object from the stream. It uses the class descriptor in the stream
to read the fields in the canonical order by name and type from the
stream. The values are assigned to the matching fields by name in the
current class. Details of the versioning mechanism can be found in Section 5.5,
"Compatible Java Type Evolution". Any field of the object that does
not appear in the stream is set to its default value. Values that appear
in the stream, but not in the object, are discarded. This occurs
primarily when a later version of a class has written additional fields
that do not occur in the earlier version. This method may only be called
from the readObject
method while restoring the fields of a
class. When called at any other time, the
NotActiveException
is thrown.
The readFields
method reads the values of the
serializable fields from the stream and makes them available via the
GetField
class. The readFields
method is only
callable from within the readObject
method of a
serializable class. It cannot be called more than once or if
defaultReadObject
has been called. The
GetFields
object uses the current object's
ObjectStreamClass
to verify the fields that can be
retrieved for this class. The GetFields
object returned by
readFields
is only valid during this call to the classes
readObject
method. The fields may be retrieved in any
order. Additional data may only be read directly from stream after
readFields
has been called.
The registerValidation
method can be called to request a
callback when the entire graph has been restored but before the object
is returned to the original caller of readObject
. The order
of validate callbacks can be controlled using the priority. Callbacks
registered with higher values are called before those with lower values.
The object to be validated must support the
ObjectInputValidation
interface and implement the
validateObject
method. It is only correct to register
validations during a call to a class's readObject
method.
Otherwise, a NotActiveException
is thrown. If the callback
object supplied to registerValidation
is null, an
InvalidObjectException
is thrown.
Starting with the Java SDK, Standard Edition, v1.3, the
readClassDescriptor
method is used to read in all
ObjectStreamClass
objects. readClassDescriptor
is called when the ObjectInputStream
expects a class
descriptor as the next item in the serialization stream. Subclasses of
ObjectInputStream
may override this method to read in class
descriptors that have been written in non-standard formats (by
subclasses of ObjectOutputStream
which have overridden the
writeClassDescriptor
method). By default, this method reads
class descriptors according to the format described in Section 6.4, "Grammar
for the Stream Format".
The resolveClass
method is called while a class is being
deserialized, and after the class descriptor has been read. Subclasses
may extend this method to read other information about the class written
by the corresponding subclass of ObjectOutputStream
. The
method must find and return the class with the given name and
serialVersionUID
. The default implementation locates the
class by calling the class loader of the closest caller of
readObject
that has a class loader. If the class cannot be
found ClassNotFoundException
should be thrown. Prior to JDK
1.1.6, the resolveClass
method was required to return the
same fully qualified class name as the class name in the stream. In
order to accommodate package renaming across releases,
method
resolveClass
only needs to return a
class with the same base class name and SerialVersionUID
in
JDK 1.1.6 and later versions.
The resolveObject
method is used by trusted subclasses
to monitor or substitute one object for another during deserialization.
Resolving objects must be enabled explicitly by calling
enableResolveObject
before calling readObject
for the first object to be resolved. Once enabled,
resolveObject
is called once for each serializable object
just prior to the first time it is being returned from
readObject
. Note that the resolveObject
method
is not called for objects of the specially handled classes,
Class
, ObjectStreamClass
, String
,
and arrays. A subclass's implementation of resolveObject
may return a substitute object that will be assigned or returned instead
of the original. The object returned must be of a type that is
consistent and assignable to every reference of the original object or
else a ClassCastException
will be thrown. All assignments
are type-checked. All references in the stream to the original object
will be replaced by references to the substitute object.
The enableResolveObject
method is called by trusted
subclasses of ObjectOutputStream
to enable the monitoring
or substitution of one object for another during deserialization.
Replacing objects is disabled until enableResolveObject
is
called with a true
value. It may thereafter be disabled by
setting it to false
. The previous setting is returned. The
enableResolveObject
method checks if the stream has
permission to request substitution during serialization. To ensure that
the private state of objects is not unintentionally exposed, only
trusted streams may use resolveObject
. Trusted classes are
those classes with a class loader equal to null or belong to a security
protection domain that provides permission to enable substitution.
If the subclass of ObjectInputStream
is not considered
part of the system domain, a line has to be added to the security policy
file to provide to a subclass of ObjectInputStream
permission to call enableResolveObject
. The
SerializablePermission
to add is
"enableSubstitution"
. AccessControlException
is thrown if the protection domain of the subclass of
ObjectStreamClass
does not have permission to
"enableSubstitution"
by calling
enableResolveObject
. See the document Java Security
Architecture (JDK 1.2) for additional information about the security
model.
The readStreamHeader
method reads and verifies the magic
number and version of the stream. If they do not match, the
StreamCorruptedMismatch
is thrown.
To override the implementation of deserialization, a subclass of
ObjectInputStream
should call the protected no-arg
ObjectInputStream
, constructor. There is a security check
within the no-arg constructor for
SerializablePermission "enableSubclassImplementation"
to
ensure that only trusted classes are allowed to override the default
implementation. This constructor does not allocate any private data for
ObjectInputStream
and sets a flag that indicates that the
final readObject
method should invoke the
readObjectOverride
method and return. All other
ObjectInputStream
methods are not final and can be directly
overridden by the subclass.
3.2 The ObjectInputStream.GetField Class
The class ObjectInputStream.GetField
provides the API
for getting the values of serializable fields. The protocol of the
stream is the same as used by defaultReadObject.
Using
readFields
to access the serializable fields does not
change the format of the stream. It only provides an alternate API to
access the values which does not require the class to have the
corresponding non-transient and non-static fields for each named
serializable field. The serializable fields are those declared using
serialPersistentFields
or if it is not declared the
non-transient and non-static fields of the object. When the stream is
read the available serializable fields are those written to the stream
when the object was serialized. If the class that wrote the stream is a
different version not all fields will correspond to the serializable
fields of the current class. The available fields can be retrieved from
the ObjectStreamClass
of the GetField
object.
The getObjectStreamClass
method returns an
ObjectStreamClass
object representing the class in the
stream. It contains the list of serializable fields.
The defaulted
method returns true if the field
is not present in the stream. An IllegalArgumentException
is thrown if the requested field is not a serializable field of the
current class.
Each get
method returns the specified serializable field
from the stream. I/O exceptions will be thrown if the underlying stream
throws an exception. An IllegalArgumentException
is thrown
if the name or type does not match the name and type of an field
serializable field of the current class. The default value is returned
if the stream does not contain an explicit value for the field.
3.3 The ObjectInputValidation Interface
This interface allows an object to be called when a complete graph of
objects has been deserialized. If the object cannot be made valid, it
should throw the ObjectInvalidException
. Any exception that
occurs during a call to validateObject
will terminate the
validation process, and the InvalidObjectException
will be
thrown.
package java.io;
public interface ObjectInputValidation
{
public void validateObject()
throws InvalidObjectException;
}
3.4 The readObject Method
For serializable objects, the readObject
method allows a
class to control the deserialization of its own fields. Here is its
signature:
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException;
Each subclass of a serializable object may define its own
readObject
method. If a class does not implement the
method, the default serialization provided by
defaultReadObject
will be used. When implemented, the class
is only responsible for restoring its own fields, not those of its
supertypes or subtypes.
The readObject
method of the class, if implemented, is
responsible for restoring the state of the class. The values of every
field of the object whether transient or not, static or not are set to
the default value for the fields type. Either
ObjectInputStream
's defaultReadObject
or
readFields
method must be called once (and only once)
before reading any optional data written by the corresponding
writeObject
method; even if no optional data is read,
defaultReadObject
or readFields
must still be
invoked once. If the readObject
method of the class
attempts to read more data than is present in the optional part of the
stream for this class, the stream will return -1
for
bytewise reads, throw an EOFException
for primitive data
reads (e.g., readInt
, readFloat
), or throw an
OptionalDataException
with the eof
field set
to true
for object reads.
The responsibility for the format, structure, and versioning of the
optional data lies completely with the class. The
@serialData
javadoc tag within the javadoc comment for the
readObject
method should be used to document the format and
structure of the optional data.
If the class being restored is not present in the stream being read,
then its readObjectNoData
method, if defined, is invoked
(instead of readObject
); otherwise, its fields are
initialized to the appropriate default values. For further detail, see
Section 3.5, "The
readObjectNoData Method".
Reading an object from the ObjectInputStream
is
analogous to creating a new object. Just as a new object's constructors
are invoked in the order from the superclass to the subclass, an object
being read from a stream is deserialized from superclass to subclass.
The readObject
or readObjectNoData
method is
called instead of the constructor for each Serializable
subclass during deserialization.
One last similarity between a constructor and a
readObject
method is that both provide the opportunity to
invoke a method on an object that is not fully constructed. Any
overridable (neither private, static nor final) method called while an
object is being constructed can potentially be overridden by a subclass.
Methods called during the construction phase of an object are resolved
by the actual type of the object, not the type currently being
initialized by either its constructor or
readObject
/readObjectNoData
method. Therefore,
calling an overridable method from within a readObject
or
readObjectNoData
method may result in the unintentional
invocation of a subclass method before the superclass has been fully
initialized.
3.5 The readObjectNoData Method
For serializable objects, the readObjectNoData
method
allows a class to control the initialization of its own fields in the
event that a subclass instance is deserialized and the serialization
stream does not list the class in question as a superclass of the
deserialized object. This may occur in cases where the receiving party
uses a different version of the deserialized instance's class than the
sending party, and the receiver's version extends classes that are not
extended by the sender's version. This may also occur if the
serialization stream has been tampered; hence,
readObjectNoData
is useful for initializing deserialized
objects properly despite a "hostile" or incomplete source stream.
private void readObjectNoData() throws ObjectStreamException;
Each serializable class may define its own
readObjectNoData
method. If a serializable class does not
define a readObjectNoData
method, then in the circumstances
listed above the fields of the class will be initialized to their
default values (as listed in The Java Language Specification); this
behavior is consistent with that of ObjectInputStream
prior
to version 1.4 of the Java 2 SDK, Standard Edition, when support for
readObjectNoData
methods was introduced. If a serializable
class does define a readObjectNoData
method and the
aforementioned conditions arise, then readObjectNoData
will
be invoked at the point during deserialization when a class-defined
readObject
method would otherwise be called had the class
in question been listed by the stream as a superclass of the instance
being deserialized.
3.6 The readExternal Method
Objects implementing java.io.Externalizable
must
implement the readExternal
method to restore the entire
state of the object. It must coordinate with its superclasses to restore
their state. All of the methods of ObjectInput
are
available to restore the object's primitive typed fields and object
fields.
public void readExternal(ObjectInput stream)
throws IOException;
Note: The readExternal
method is
public, and it raises the risk of a client being able to overwrite an
existing object from a stream. The class may add its own checks to
insure that this is only called when appropriate.
A new stream protocol version has been introduced in JDK 1.2 to
correct a problem with Externalizable
objects. The old
definition of Externalizable
objects required the local
virtual machine to find a readExternal
method to be able to
properly read an Externalizable
object from the stream. The
new format adds enough information to the stream protocol so
serialization can skip an Externalizable
object when the
local readExternal
method is not available. Due to class
evolution rules, serialization must be able to skip an
Externalizable
object in the input stream if there is not a
mapping for the object using the local classes.
An additional benefit of the new Externalizable
stream
format is that ObjectInputStream
can detect attempts to
read more External data than is available, and can also skip by any data
that is left unconsumed by a readExternal
method. The
behavior of ObjectInputStream
in response to a read past
the end of External data is the same as the behavior when a
class-defined readObject
method attempts to read past the
end of its optional data: bytewise reads will return -1
,
primitive reads will throw EOFException
s, and object reads
will throw OptionalDataException
s with the eof
field set to true
.
Due to the format change, JDK 1.1.6 and earlier releases are not able
to read the new format. StreamCorruptedException
is thrown
when JDK 1.1.6 or earlier attempts to read an
Externalizable
object from a stream written in
PROTOCOL_VERSION_2
. Compatibility issues are discussed in
more detail in Section
6.3, "Stream Protocol Versions".
3.7 The readResolve Method
For Serializable and Externalizable classes, the
readResolve
method allows a class to replace/resolve the
object read from the stream before it is returned to the caller. By
implementing the readResolve
method, a class can directly
control the types and instances of its own instances being deserialized.
The method is defined as follows:
ANY-ACCESS-MODIFIER Object readResolve()
throws ObjectStreamException;
The readResolve
method is called when
ObjectInputStream
has read an object from the stream and is
preparing to return it to the caller. ObjectInputStream
checks whether the class of the object defines the
readResolve
method. If the method is defined, the
readResolve
method is called to allow the object in the
stream to designate the object to be returned. The object returned
should be of a type that is compatible with all uses. If it is not
compatible, a ClassCastException
will be thrown when the
type mismatch is discovered.
For example, a Symbol
class could be created for which
only a single instance of each symbol binding existed within a virtual
machine. The readResolve
method would be implemented to
determine if that symbol was already defined and substitute the
preexisting equivalent Symbol
object to maintain the
identity constraint. In this way the uniqueness of Symbol
objects can be maintained across serialization.
Note: The readResolve
method is not
invoked on the object until the object is fully constructed, so any
references to this object in its object graph will not be updated to the
new object nominated by readResolve
. However, during the
serialization of an object with the writeReplace
method,
all references to the original object in the replacement object's object
graph are replaced with references to the replacement object. Therefore
in cases where an object being serialized nominates a replacement object
whose object graph has a reference to the original object,
deserialization will result in an incorrect graph of objects.
Furthermore, if the reference types of the object being read (nominated
by writeReplace
) and the original object are not
compatible, the construction of the object graph will raise a
ClassCastException
.