public abstract class DefinitionalObject extends Object implements ICloneWithContext, IFreezable, IEquatableDefinition, IEnumerateDependencies
The base class for all definitional objects. A definitional object has a few characteristics:
DefinitionalObject.freeze()
method.
Attempts to modify a frozen object will result in an ObjectFrozenException
.
DefinitionalObject.clone(agi.foundation.infrastructure.CopyContext)
method.
DefinitionalObject.isSameDefinition(java.lang.Object)
method. Definitional objects also
have a hash code based on their definition that can be retrieved by calling
DefinitionalObject.getDefinitionHashCode()
. This hash code is guaranteed not to change during
the lifetime of the object, even if properties of the object change. As a result, not
all equivalent objects will have the same hash code, but objects that are created
in the same way will have the same hash code. This is useful in many situations, and
is better than the alternative of allowing an object to get lost in a dictionary when its
hash code changes.
Modifier | Constructor and Description |
---|---|
protected |
DefinitionalObject()
Initializes a new instance.
|
protected |
DefinitionalObject(DefinitionalObject existingInstance,
CopyContext context)
Initializes a new instance as a copy of an existing instance.
|
Modifier and Type | Method and Description |
---|---|
static boolean |
areSameDefinition(boolean first,
boolean second)
Determines if two booleans have the same definition.
|
static boolean |
areSameDefinition(double first,
double second)
Determines if two doubles have the same definition.
|
static boolean |
areSameDefinition(IEquatableDefinition first,
Object second)
Determines if two objects have the same definition or are both
null . |
static boolean |
areSameDefinition(int first,
int second)
Determines if two integers have the same definition.
|
static boolean |
areSameDefinition(Object first,
Object second)
Determines if two objects have the same definition or are both
null . |
protected abstract boolean |
checkForSameDefinition(DefinitionalObject other)
Checks to determine if another instance has the same definition as this instance and
returns
true if it does. |
abstract Object |
clone(CopyContext context)
Clones this object using the specified context.
|
static <T> boolean |
collectionItemsAreSameDefinition(Iterable<T> first,
Iterable<T> second)
Determines if two collections contain items with the same definition and in the same order,
or are both
null . |
static <T> boolean |
collectionItemsAreSameDefinition(T[] first,
T[] second)
Determines if two collections contain items with the same definition and in the same order,
or are both
null . |
static <T> boolean |
collectionItemsAreSameDefinition(TimeIntervalCollection1<T> first,
TimeIntervalCollection1<T> second)
Determines if two interval collections contain items with the same definition and in the same order,
or are both
null . |
protected int |
computeCurrentDefinitionHashCode()
Computes a hash code based on the current properties of this object.
|
static <TKey,TValue> |
dictionaryItemsAreSameDefinition(Map<TKey,TValue> first,
Map<TKey,TValue> second)
Determines if two dictionaries contain items with the same definition, or are both
null . |
void |
enumerateDependencies(DependencyEnumerator enumerator)
Enumerates the dependencies of this object by calling
DependencyEnumerator#enumerate(T) for each object that this object directly depends upon. |
void |
freeze()
Freezes this object.
|
protected void |
freezeAggregatedObjects()
Called by
DefinitionalObject.freeze() to also freeze any objects that are considered to be a part of this object. |
static int |
getCollectionHashCode(double[] array)
Computes a hash code from the items in an array.
|
static <T> int |
getCollectionHashCode(Iterable<T> collection)
Computes a hash code from the items in an enumerable collection.
|
static <T> int |
getCollectionHashCode(T[] array)
Computes a hash code from the items in an array.
|
int |
getDefinitionHashCode()
Gets a hash code representing the definition of this object.
|
static int |
getDefinitionHashCode(boolean o)
Gets a hash code safely.
|
static int |
getDefinitionHashCode(double o)
Gets a hash code safely.
|
static int |
getDefinitionHashCode(IEquatableDefinition o)
Gets a hash code safely.
|
static int |
getDefinitionHashCode(int o)
Gets a hash code safely.
|
static <T> int |
getDefinitionHashCode(T o)
Gets a hash code safely.
|
static <TKey,TValue> |
getDictionaryHashCode(Map<TKey,TValue> dictionary)
Computes a hash code from the items in a dictionary.
|
boolean |
getIsFrozen()
Gets a value indicating whether this object is frozen.
|
boolean |
isSameDefinition(Object other)
Determines if this object has the same definition as another object.
|
protected void |
throwIfFrozen()
|
protected DefinitionalObject()
protected DefinitionalObject(@Nonnull DefinitionalObject existingInstance, @Nonnull CopyContext context)
See ICloneWithContext.clone(CopyContext)
for more information about how to implement this constructor
in a derived class.
existingInstance
- The existing instance to copy.context
- A CopyContext
that controls the depth of the copy.ArgumentNullException
- Thrown when existingInstance
or context
is null
.public abstract Object clone(CopyContext context)
This method should be implemented to call a copy constructor on the class of the
object being cloned. The copy constructor should take the CopyContext
as a parameter
in addition to the existing instance to copy. The copy constructor should first call
CopyContext.addObjectMapping(T, T)
to identify the newly constructed instance
as a copy of the existing instance. It should then copy all fields, using
CopyContext.updateReference(T)
to copy any reference fields.
A typical implementation of ICloneWithContext
:
public static class MyClass implements ICloneWithContext {
public MyClass(MyClass existingInstance, CopyContext context) {
context.addObjectMapping(existingInstance, this);
someReference = context.updateReference(existingInstance.someReference);
}
@Override
public final Object clone(CopyContext context) {
return new MyClass(this, context);
}
private Object someReference;
}
In general, all fields that are reference types should be copied with a call to
CopyContext.updateReference(T)
. There are a couple of exceptions:
If one of these exceptions applies, the CopyContext
should be given an opportunity
to update the reference before the reference is copied explicitly. Use
CopyContext.updateReference(T)
to update the reference. If CopyContext.updateReference(T)
returns
the original object, indicating that the context does not have a replacement registered,
then copy the object manually by invoking a Clone method, a copy constructor, or by manually
constructing a new instance and copying the values.
alwaysCopy = context.updateReference(existingInstance.alwaysCopy);
if (existingInstance.alwaysCopy != null && alwaysCopy == existingInstance.alwaysCopy) {
alwaysCopy = (AlwaysCopy) existingInstance.alwaysCopy.clone(context);
}
If you are implementing an evaluator (a class that implements IEvaluator
), the
IEvaluator.updateEvaluatorReferences(agi.foundation.infrastructure.CopyContext)
method shares some responsibilities with the
copy context constructor. Code duplication can be avoided by doing the following:
CopyContext.updateReference(T)
. You should still call CopyContext.updateReference(T)
on any references to
non-evaluators.
IEvaluator.updateEvaluatorReferences(agi.foundation.infrastructure.CopyContext)
as the last line in the constructor and pass it the
same CopyContext
passed to the constructor.
IEvaluator.updateEvaluatorReferences(agi.foundation.infrastructure.CopyContext)
as normal. See the reference documentation for
IEvaluator.updateEvaluatorReferences(agi.foundation.infrastructure.CopyContext)
for more information on implementing that method.
public MyClass(MyClass existingInstance, CopyContext context) {
super(existingInstance, context);
someReference = context.updateReference(existingInstance.someReference);
evaluatorReference = existingInstance.evaluatorReference;
updateEvaluatorReferences(context);
}
@Override
public void updateEvaluatorReferences(CopyContext context) {
evaluatorReference = context.updateReference(evaluatorReference);
}
@Override
public Object clone(CopyContext context) {
return new MyClass(this, context);
}
private Object someReference;
private IEvaluator evaluatorReference;
clone
in interface ICloneWithContext
context
- The context to use to perform the copy.public final boolean isSameDefinition(Object other)
This method is very similar to Object.equals(Object)
except that it explicitly considers
the "definitions" of the two objects for objects that do not typically act like values. The definition of
an object typically includes all of the fields of the object.
isSameDefinition
in interface IEquatableDefinition
other
- The other instance to compare to this one.true
if this object has the same definition as the specified one.
false
if the other object is null
, a different type
than this one, or if this object and the specified one have different definitions.protected abstract boolean checkForSameDefinition(DefinitionalObject other)
true
if it does. Derived classes MUST override this method and check
all new fields introduced by the derived class for definitional equivalence. It is NOT necessary
to check base class fields because the base class will already have done that. When overriding this method,
you should NOT call the base implementation because it will return false
for all derived-class instances.
Derived classes should check the type of other
to preserve the symmetric nature of IEquatableDefinition.isSameDefinition(java.lang.Object)
.other
- The other instance to compare to this one.true
if the two objects are defined equivalently; otherwise false
.public final int getDefinitionHashCode()
This method is very similar to Object.hashCode()
except that it explicitly includes
the "definition" of the object even if the object does not typically act like a value. The definition of
an object typically includes all of the fields of the object. The value returned by this method should
NOT change. This means that two objects for which DefinitionalObject.isSameDefinition(java.lang.Object)
returns true
will not necessarily have the same hash code if one or the other was changed after the hash code was first
obtained.
getDefinitionHashCode
in interface IEquatableDefinition
public static int getDefinitionHashCode(@Nullable IEquatableDefinition o)
null
, a valid hash code is still returned.o
- The object for which to obtain the hash code.o
is null
.public static <T> int getDefinitionHashCode(@Nullable T o)
null
, a valid hash code is still returned.T
- The type of o
.o
- The object for which to obtain the hash code.o
is null
.public static int getDefinitionHashCode(double o)
Double.hashCode()
and is provided for convenience.o
- The double for which to obtain the hash code.public static int getDefinitionHashCode(int o)
Integer.hashCode()
and is provided for convenience.o
- The integer for which to obtain the hash code.public static int getDefinitionHashCode(boolean o)
Boolean.hashCode()
and is provided for convenience.o
- The boolean for which to obtain the hash code.protected int computeCurrentDefinitionHashCode()
DefinitionalObject.checkForSameDefinition(agi.foundation.infrastructure.DefinitionalObject)
method.public void enumerateDependencies(DependencyEnumerator enumerator)
DependencyEnumerator#enumerate(T)
for each object that this object directly depends upon.
Derived classes which contain additional dependencies MUST override this method, call the base
implementation, and enumerate dependencies introduced by the derived class.enumerateDependencies
in interface IEnumerateDependencies
enumerator
- The enumerator that is informed of the dependencies of this object.protected void freezeAggregatedObjects()
DefinitionalObject.freeze()
to also freeze any objects that are considered to be a part of this object.
Derived classes which contain additional aggregated objects MUST override this method, call the base
implementation, and freeze aggregated objects introduced by the derived class. The objects that need to be
frozen in this method are frequently created in this object's constructor and are not settable via
properties.public final void freeze()
ObjectFrozenException
.freeze
in interface IFreezable
public final boolean getIsFrozen()
ObjectFrozenException
will be thrown if an attempt is made to do so.getIsFrozen
in interface IFreezable
protected final void throwIfFrozen()
ObjectFrozenException
if this object IsFrozen
(get
).
This method should be called from any method or property that modifies this object.public static boolean areSameDefinition(@Nullable IEquatableDefinition first, @Nullable Object second)
null
.first
- The first object, which may be null.second
- The second object, which may be null.true
if the two objects have the same definition; otherwise false
.public static boolean areSameDefinition(@Nullable Object first, @Nullable Object second)
null
.
If first
implements IEquatableDefinition
, this method will use
IEquatableDefinition.isSameDefinition(java.lang.Object)
to compare them.
Otherwise, it will use Objects.equals(Object,Object)
.
first
- The first object, which may be null.second
- The second object, which may be null.true
if the two objects have the same definition; otherwise false
.public static boolean areSameDefinition(double first, double second)
first
- The first double.second
- The second double.true
if the two doubles have the same definition; otherwise false
.public static boolean areSameDefinition(int first, int second)
first
- The first integer.second
- The second integer.true
if the two integers have the same definition; otherwise false
.public static boolean areSameDefinition(boolean first, boolean second)
first
- The first boolean.second
- The second boolean.true
if the two booleans have the same definition; otherwise false
.public static <T> boolean collectionItemsAreSameDefinition(@Nullable Iterable<T> first, @Nullable Iterable<T> second)
null
.
If the items in the collection implement IEquatableDefinition
, this method will use
IEquatableDefinition.isSameDefinition(java.lang.Object)
to compare them. Otherwise, it will
use Objects.equals(Object,Object)
.
T
- The type of the items in the collections.first
- The first collection, which may be null
.second
- The second collection, which may be null
.true
if the two collections contain items with the same definition and in the
same order; otherwise false
.public static <T> boolean collectionItemsAreSameDefinition(@Nullable T[] first, @Nullable T[] second)
null
.
If the items in the collection implement IEquatableDefinition
, this method will use
IEquatableDefinition.isSameDefinition(java.lang.Object)
to compare them. Otherwise, it will
use Objects.equals(Object,Object)
.
T
- The type of the items in the collections.first
- The first collection, which may be null
.second
- The second collection, which may be null
.true
if the two collections contain items with the same definition and in the
same order; otherwise false
.public static <T> boolean collectionItemsAreSameDefinition(@Nullable TimeIntervalCollection1<T> first, @Nullable TimeIntervalCollection1<T> second)
null
.
If T
implements IEquatableDefinition
, this method will use
IEquatableDefinition.isSameDefinition(java.lang.Object)
to compare them. Otherwise, it will
use Objects.equals(Object,Object)
.
T
- The type of the data in the interval collections.first
- The first collection, which may be null.second
- The second collection, which may be null.true
if the two collections contain items with the same definition and in the
same order; otherwise false
.public static <TKey,TValue> boolean dictionaryItemsAreSameDefinition(@Nullable Map<TKey,TValue> first, @Nullable Map<TKey,TValue> second)
null
.
If an item in first
implements IEquatableDefinition
, this method will use
IEquatableDefinition.isSameDefinition(java.lang.Object)
to compare the objects. Otherwise, it will use the
Object.equals(Object)
method.
TKey
- The type of the keys in the dictionary.TValue
- The type of the values in the dictionary.first
- The first dictionary, which may be null.second
- The second dictionary, which may be null.true
if the two dictionaries contain items with the same definition; otherwise false
.public static <T> int getCollectionHashCode(@Nullable Iterable<T> collection)
T
- The type of item in the collection.collection
- The collection containing the items for which to compute the hash code.public static <T> int getCollectionHashCode(@Nullable T[] array)
T
- The type of item in the array.array
- The array containing the items for which to compute the hash code.public static int getCollectionHashCode(@Nullable double[] array)
array
- The array containing the items for which to compute the hash code.public static <TKey,TValue> int getDictionaryHashCode(@Nullable Map<TKey,TValue> dictionary)
TKey
- The type of the keys in the dictionary.TValue
- The type of the values in the dictionary.dictionary
- The dictionary containing the items for which to compute the hash code.