blob: 2b1f0f4b8b9fb0e09469dca86e1c46eb3dce490f [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>ComparisonModes</title>
<meta charset="UTF-8"/>
<link rel="stylesheet" type="text/css" href="../book.css"/>
</head>
<body>
<!--
Content below this point is copied in "/asf-staging/book/en/developer-guide.html" file
by the `org.apache.sis.buildtools.book` class in `buildSrc`.
-->
<section>
<header>
<h2 id="ComparisonModes">Comparison modes of objects</h2>
</header>
<p>
There are various opinions on how to implement Java standard’s <code>Object.equals(Object)</code> 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 <code>equals(Object)</code> and <code>hashCode()</code> methods.
This approach is common in <code>java.util.Collection</code> 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 <code>equals(Object)</code> and <code>hashCode()</code> method contracts:
</p>
<ul>
<li><code>A.equals(B)</code> implies <code>B.equals(A)</code> (symmetry);</li>
<li><code>A.equals(B)</code> and <code>B.equals(C)</code> implies <code>A.equals(C)</code> (transitivity);</li>
<li><code>A.equals(B)</code> implies <code>A.hashCode() == B.hashCode()</code>.</li>
</ul>
<p>
For example, these three constraints are violated if <var>A</var> (and eventually <var>C</var>) can contain attributes
which <var>B</var> ignores.
To bypass this problem, an alternative approach is to require that the objects compared by the
<code>Object.equals(Object)</code> method be of the same class; in other words, <code>A.getClass() == B.getClass()</code>.
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 <code>HashSet</code> or used as keys in a <code>HashMap</code>,
we would need a stricter adherence to the <code>equals(Object)</code> and <code>hashCode()</code> 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.
</p>
<p>
In order to allow developers a certain amount of flexibility, many classes in the <abbr>SIS</abbr>
library implement the <code>org.apache.sis.util.LenientComparable</code> interface,
which defines a <code class="SIS">equals(Object, ComparisonMode)</code> method.
The principle modes of comparison are:
</p>
<ul>
<li><p>
<b><code class="SIS">STRICT</code></b> — The objects compared must share the same class and have exactly equal attributes,
including any possible public attributes specific to the implementation.
</p></li>
<li><p>
<b><code class="SIS">BY_CONTRACT</code></b> — 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.
</p></li>
<li><p>
<b><code class="SIS">IGNORE_METADATA</code></b> — Like <code class="SIS">BY_CONTRACT</code>,
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.
</p></li>
<li><p>
<b><code class="SIS">APPROXIMATIVE</code></b> — Like <code class="SIS">IGNORE_METADATA</code>,
but tolerates minor discrepancies in numerical values.
</p></li>
</ul>
<p>
The default mode, used in all <code>equals(Object)</code> methods in <abbr>SIS</abbr>,
is <code class="SIS">STRICT</code>. This mode is chosen for a safe operation — particularly with <code>HashMap</code> —
without the need to rigorously define <code>equals(Object)</code> and <code>hashCode()</code> operations in every interface.
With this mode, the order of objects (<code>A.equals(B)</code> or <code>B.equals(A)</code>) is unimportant.
It is, however, the only mode that offers this guarantee.
In the expression <code>A.equals(B)</code>, the <code class="SIS">BY_CONTRACT</code> mode
(and so by extension all other modes that depend on it) only compares the properties known to <code>A</code>,
regardless of whether <code>B</code> knows more.
</p>
</section>
</body>
</html>