Allow any Streamable class to use the "we'll call your single non-zero-argument
constructor with zero/null values and then overwrite any fields initialized with them immediately afterward" approach. If a Streamable has a zero-args constructor, that is still used, so all the existing Streamable classes will still function exactly as before. But new classes (or old classes undergoing scrutiny) can opt to reduce their boilerplate as long as they know they cope with the zero/null-valued constructor call. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6573 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -25,9 +25,9 @@ package com.threerings.io;
|
||||
* Marks an object as streamable, meaning that it can be written to {@link ObjectOutputStream}
|
||||
* instances and read from {@link ObjectInputStream} instances.
|
||||
*
|
||||
* <p> All non-{@code transient} fields will be automatically written and restored for a {@link
|
||||
* Streamable} instance. Classes that wish to stream transient fields or customize the streaming
|
||||
* process should implement methods with the following signatures: </p>
|
||||
* <p> All non-{@code transient}, non-{@link NotStreamable} fields will be automatically written
|
||||
* and restored for a {@link Streamable} instance. Classes that wish to stream transient fields or
|
||||
* customize the streaming process should implement methods with the following signatures: </p>
|
||||
*
|
||||
* <p><code>
|
||||
* public void writeObject ({@link ObjectOutputStream} out);
|
||||
@@ -38,6 +38,14 @@ package com.threerings.io;
|
||||
* ObjectOutputStream#defaultWriteObject} and {@link ObjectInputStream#defaultReadObject} from
|
||||
* within their {@code writeObject} and {@code readObject} methods to perform the standard
|
||||
* streaming in addition to their customized behavior.</p>
|
||||
*
|
||||
* <p>Streamable classes must <em>either</em> have a zero-argument constructor, in which case any
|
||||
* number of other constructors are allowed, but the zero-argument constructor will be used when
|
||||
* unserializing an instance; or they must have exactly one non-zero-argument constructor, and that
|
||||
* constructor will be called when unserializing an instance with "zero/null" values (meaning all
|
||||
* primitive types will be passed the appropriate zero value, and all reference types will be
|
||||
* passed null. This latter approach can even be used by classes with final fields, as the
|
||||
* zero/null values will be overwritten during unstreaming.</p>
|
||||
*/
|
||||
public interface Streamable
|
||||
{
|
||||
|
||||
@@ -421,33 +421,26 @@ public abstract class Streamer
|
||||
}
|
||||
}
|
||||
|
||||
// if we're an anonymous closure, we also support having a single constructor to which
|
||||
// we'll pass zero-valued arguments, which will then be overwritten by unstreaming
|
||||
if (Streamable.Closure.class.isAssignableFrom(_target) && _ctor == null) {
|
||||
Constructor<?>[] ctors = _target.getDeclaredConstructors();
|
||||
if (ctors.length > 1) {
|
||||
throw new RuntimeException(
|
||||
"Streamable closure classes must have either a zero-argument constructor " +
|
||||
"or a single argument-taking constructor; multiple argument-taking " +
|
||||
"constructors are not allowed [class=" + _target.getName() + "]");
|
||||
}
|
||||
_ctor = ctors[0];
|
||||
_ctor.setAccessible(true);
|
||||
|
||||
// we pass bogus arguments to it (because unstreaming will overwrite our bogus
|
||||
// values with the real values)
|
||||
Class<?>[] ptypes = _ctor.getParameterTypes();
|
||||
_ctorArgs = new Object[ptypes.length];
|
||||
for (int ii = 0; ii < ptypes.length; ii++) {
|
||||
// this will be the appropriately typed zero, or null
|
||||
_ctorArgs[ii] = Defaults.defaultValue(ptypes[ii]);
|
||||
}
|
||||
// otherwise there should be a single non-zero-argument constructor, which we'll call
|
||||
// with zero-valued arguments at unstreaming time, which will then be overwritten by
|
||||
// readObject()
|
||||
Constructor<?>[] ctors = _target.getDeclaredConstructors();
|
||||
if (ctors.length > 1) {
|
||||
throw new RuntimeException(
|
||||
"Streamable closure classes must have either a zero-argument constructor " +
|
||||
"or a single argument-taking constructor; multiple argument-taking " +
|
||||
"constructors are not allowed [class=" + _target.getName() + "]");
|
||||
}
|
||||
_ctor = ctors[0];
|
||||
_ctor.setAccessible(true);
|
||||
|
||||
// make sure we found a constructor
|
||||
if (_ctor == null) {
|
||||
throw new RuntimeException("Unable to find applicable ctor " +
|
||||
"[class=" + _target.getName() + "]");
|
||||
// we pass bogus arguments to it (because unstreaming will overwrite our bogus
|
||||
// values with the real values)
|
||||
Class<?>[] ptypes = _ctor.getParameterTypes();
|
||||
_ctorArgs = new Object[ptypes.length];
|
||||
for (int ii = 0; ii < ptypes.length; ii++) {
|
||||
// this will be the appropriately typed zero, or null
|
||||
_ctorArgs[ii] = Defaults.defaultValue(ptypes[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user