There are various opinions on how to implement Java standard’s Object.equals(Object)
method.
According to some, it should be possible to compare different implementations of the same interface or base class.
But to follow this policy, each interface or base class’s javadoc must define the algorithms that all implementations
shall use for their equals(Object)
and hashCode()
methods.
This approach is common in java.util.Collection
and its child interfaces.
Transferring this approach to certain GeoAPI interfaces, however, would be a difficult task,
and would probably not be followed in many implementations.
Moreover, it comes at the expense of being able to take into account supplementary attributes in the child interfaces,
unless this possibility has been specified in the parent interface.
This constraint arises from the following points of the equals(Object)
and hashCode()
method contracts:
A.equals(B)
implies B.equals(A)
(symmetry);A.equals(B)
and B.equals(C)
implies A.equals(C)
(transitivity);A.equals(B)
implies A.hashCode() == B.hashCode()
.
For example, these three constraints are violated if A (and eventually C) can contain attributes
which B ignores.
To bypass this problem, an alternative approach is to require that the objects compared by the
Object.equals(Object)
method be of the same class; in other words, A.getClass() == B.getClass()
.
This approach is sometimes regarded as contrary to the principles of object oriented programming.
In practice, for relatively complex applications, the important accorded to these principles depends on the context
in which the objects are compared:
if the objects are added to a HashSet
or used as keys in a HashMap
,
we would need a stricter adherence to the equals(Object)
and hashCode()
contract.
But if the developer is comparing the objects his- or herself, for example to check that the relevant information has been changed,
then the constraints of symmetry, transitivity or coherence with the hash values may be of little interest.
More permissive comparisons may be desirable, sometimes going so far as to tolerate minor discrepancies in numerical values.
In order to allow developers a certain amount of flexibility, many classes in the SIS
library implement the org.apache.sis.util.LenientComparable
interface,
which defines a equals(Object, ComparisonMode)
method.
The principle modes of comparison are:
STRICT
— The objects compared must share the same class and have exactly equal attributes,
including any possible public attributes specific to the implementation.
BY_CONTRACT
— The objects compared must implement the same GeoAPI (or other standard)
interface, but need not be of the same implementation class.
Only the attributes defined in the interface are compared;
all other attributes specific to the implementation — even if they are public — are ignored.
IGNORE_METADATA
— Like BY_CONTRACT
,
but only compares attributes that influence the operations (numeric calculations or otherwise) performed by the object.
For example, in a geodesic datum, the longitude (in relation to Greenwich) of the original meridian
would be taken into account, while the name of the meridian would be ignored.
APPROXIMATIVE
— Like IGNORE_METADATA
,
but tolerates minor discrepancies in numerical values.
The default mode, used in all equals(Object)
methods in SIS,
is STRICT
. This mode is chosen for a safe operation — particularly with HashMap
—
without the need to rigorously define equals(Object)
and hashCode()
operations in every interface.
With this mode, the order of objects (A.equals(B)
or B.equals(A)
) is unimportant.
It is, however, the only mode that offers this guarantee.
In the expression A.equals(B)
, the BY_CONTRACT
mode
(and so by extension all other modes that depend on it) only compares the properties known to A
,
regardless of whether B
knows more.