View Javadoc
1   package org.djunits.unit;
2   
3   import java.io.Serializable;
4   import java.util.ArrayList;
5   import java.util.Arrays;
6   import java.util.HashMap;
7   import java.util.HashSet;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Set;
11  
12  import org.djunits.locale.Localization;
13  import org.djunits.unit.scale.Scale;
14  import org.djunits.unit.scale.StandardScale;
15  import org.djunits.unit.unitsystem.UnitSystem;
16  
17  /**
18   * All units are internally <i>stored</i> relative to a standard unit with conversion factor. This means that e.g., a meter is
19   * stored with conversion factor 1.0, whereas kilometer is stored with a conversion factor 1000.0. This means that if we want to
20   * express a length meter in kilometers, we have to <i>divide</i> by the conversion factor.
21   * <p>
22   * Copyright (c) 2015-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
23   * BSD-style license. See <a href="http://djunits.org/docs/license.html">DJUNITS License</a>.
24   * <p>
25   * $LastChangedDate: 2019-04-22 12:49:59 +0200 (Mon, 22 Apr 2019) $, @version $Revision: 401 $, by $Author: averbraeck $,
26   * initial version May 15, 2014 <br>
27   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
28   * @param <U> the unit for transformation reasons
29   */
30  public abstract class Unit<U extends Unit<U>> implements Serializable
31  {
32      /** */
33      private static final long serialVersionUID = 20140607;
34  
35      /** The key to the locale file for the abbreviation of the unit, or null when it is a user-defined unit. */
36      private final String abbreviationKey;
37  
38      /**
39       * The long name of the unit in case it does not exist in the locale file, e.g. when defining a run-time unit. The name will
40       * be null if the locale has to be used, i.e. for standard units.
41       */
42      private final String name;
43  
44      /**
45       * The abbreviation of the unit in case it does not exist in the locale file, e.g. when defining a run-time unit. The
46       * abbreviation will be null if the locale has to be used, i.e. for standard units.
47       */
48      private final String abbreviation;
49  
50      /** The unit system, e.g. SI or Imperial. */
51      private final UnitSystem unitSystem;
52  
53      /** The scale to use to convert between this unit and the standard (e.g., SI) unit. */
54      private final Scale scale;
55  
56      /** SI unit information. */
57      private SICoefficients siCoefficients;
58  
59      /** A static map of all defined coefficient strings, to avoid double creation and allow lookup. */
60      private static final Map<String, SICoefficients> SI_COEFFICIENTS = new HashMap<String, SICoefficients>();
61  
62      /** A static map of all defined coefficient strings, mapped to the existing units. */
63      private static final Map<String, Map<Class<Unit<?>>, Unit<?>>> SI_UNITS =
64              new HashMap<String, Map<Class<Unit<?>>, Unit<?>>>();
65  
66      /** A static map of all defined units. */
67      private static final Map<String, Set<Unit<?>>> UNITS = new HashMap<String, Set<Unit<?>>>();
68  
69      /** Has this class been initialized? */
70      private static boolean standardUnitsInitialized = false;
71  
72      /** Standard (SI) unit or not? */
73      private boolean baseSIUnit;
74  
75      /** The array of the names of the standard units. */
76      public static final String[] STANDARD_UNITS = new String[] {"AbsoluteTemperatureUnit", "AccelerationUnit", "AngleSolidUnit",
77              "AngleUnit", "AreaUnit", "DensityUnit", "DimensionlessUnit", "DirectionUnit", "DurationUnit",
78              "ElectricalChargeUnit", "ElectricalCurrentUnit", "ElectricalPotentialUnit", "ElectricalResistanceUnit",
79              "EnergyUnit", "FlowMassUnit", "FlowVolumeUnit", "ForceUnit", "FrequencyUnit", "LengthUnit", "LinearDensityUnit",
80              "MassUnit", "MoneyUnit", "MoneyPerAreaUnit", "MoneyPerEnergyUnit", "MoneyPerLengthUnit", "MoneyPerMassUnit",
81              "MoneyPerDurationUnit", "MoneyPerVolumeUnit", "PositionUnit", "PowerUnit", "PressureUnit", "SpeedUnit",
82              "TemperatureUnit", "TimeUnit", "TorqueUnit", "VolumeUnit"};
83  
84      /** the cached hashcode. */
85      private final int cachedHashCode;
86  
87      /** The default locale. */
88      private static Localization localization = new Localization("localeunit");
89  
90      /** The cached default locale information. */
91      private String[] cachedDefaultLocaleInfo;
92  
93      /**
94       * Force all units to be loaded so we can use static lookup functions for the standard units.
95       */
96      private static void initializeStandardUnits()
97      {
98          for (String className : STANDARD_UNITS)
99          {
100             try
101             {
102                 Class.forName("org.djunits.unit." + className);
103             }
104             catch (Exception exception)
105             {
106                 // TODO professional logging of errors
107                 System.err.println("Could not load class org.djunits.unit." + className);
108             }
109         }
110         standardUnitsInitialized = true;
111     }
112 
113     /**
114      * Build a standard unit and create the fields for that unit. For a standard unit, a UnitException is silently ignored.
115      * @param abbreviationKey String; the key to the locale file for the abbreviation of the unit
116      * @param unitSystem UnitSystem; the unit system, e.g. SI or Imperial
117      */
118     protected Unit(final String abbreviationKey, final UnitSystem unitSystem)
119     {
120         this.scale = StandardScale.SCALE;
121         this.baseSIUnit = true;
122         this.abbreviationKey = abbreviationKey;
123         this.name = null;
124         this.abbreviation = null;
125         this.cachedDefaultLocaleInfo = localization.getDefaultString(this.abbreviationKey).split("\\|");
126         this.unitSystem = unitSystem;
127         this.cachedHashCode = generateHashCode();
128         try
129         {
130             addUnit(this);
131         }
132         catch (UnitException ue)
133         {
134             // silently ignore.
135         }
136     }
137 
138     /**
139      * Build a standard unit with a specific conversion scale to/from the standard unit. For a standard unit, a UnitException is
140      * silently ignored.
141      * @param abbreviationKey String; the key to the locale file for the abbreviation of the unit, otherwise the abbreviation
142      *            itself
143      * @param unitSystem UnitSystem; the unit system, e.g. SI or Imperial
144      * @param scale Scale; the conversion scale to use for this unit
145      */
146     protected Unit(final String abbreviationKey, final UnitSystem unitSystem, final Scale scale)
147     {
148         this.scale = scale;
149         this.baseSIUnit = scale.isBaseSIScale();
150         this.abbreviationKey = abbreviationKey;
151         this.name = null;
152         this.abbreviation = null;
153         this.cachedDefaultLocaleInfo = localization.getDefaultString(this.abbreviationKey).split("\\|");
154         this.unitSystem = unitSystem;
155         this.cachedHashCode = generateHashCode();
156         try
157         {
158             addUnit(this);
159         }
160         catch (UnitException ue)
161         {
162             // silently ignore
163         }
164     }
165 
166     /**
167      * Build a user-defined unit and create the fields for that unit. This unit is a user-defined unit where the localization
168      * files do not have an entry. A UnitException is thrown as a RunTimeException.
169      * @param name String; the key to the locale file for the long name of the unit, otherwise the name itself
170      * @param abbreviation String; the key to the locale file for the abbreviation of the unit, otherwise the abbreviation
171      *            itself
172      * @param unitSystem UnitSystem; the unit system, e.g. SI or Imperial
173      */
174     protected Unit(final String name, final String abbreviation, final UnitSystem unitSystem)
175     {
176         this.scale = StandardScale.SCALE;
177         this.baseSIUnit = true;
178         this.abbreviationKey = null;
179         this.name = name;
180         this.abbreviation = abbreviation;
181         this.unitSystem = unitSystem;
182         this.cachedHashCode = generateHashCode();
183         try
184         {
185             addUnit(this);
186         }
187         catch (UnitException ue)
188         {
189             throw new RuntimeException(ue);
190         }
191     }
192 
193     /**
194      * Build a user-defined unit with a specific conversion scale to/from the standard unit. This unit is a user-defined unit
195      * where the localization files do not have an entry. A UnitException is thrown as a RunTimeException.
196      * @param name String; the key to the locale file for the long name of the unit, otherwise the name itself
197      * @param abbreviation String; the key to the locale file for the abbreviation of the unit, otherwise the abbreviation
198      *            itself
199      * @param unitSystem UnitSystem; the unit system, e.g. SI or Imperial
200      * @param scale Scale; the conversion scale to use for this unit
201      */
202     protected Unit(final String name, final String abbreviation, final UnitSystem unitSystem, final Scale scale)
203     {
204         this.scale = scale;
205         this.baseSIUnit = scale.isBaseSIScale();
206         this.abbreviationKey = null;
207         this.name = name;
208         this.abbreviation = abbreviation;
209         this.unitSystem = unitSystem;
210         this.cachedHashCode = generateHashCode();
211         try
212         {
213             addUnit(this);
214         }
215         catch (UnitException ue)
216         {
217             throw new RuntimeException(ue);
218         }
219     }
220 
221     /**
222      * Report if this unit support localization.
223      * @return boolean; true if this unit supports localization; false if it does not
224      */
225     public final boolean isLocalizable()
226     {
227         return this.abbreviationKey != null;
228     }
229 
230     /**
231      * Add a unit to the overview collection of existing units, and resolve the coefficients.
232      * @param unit Unit&lt;U&gt;; the unit to add. It will be stored in a set belonging to the simple class name String, e.g.
233      *            "ForceUnit".
234      * @throws UnitException when parsing or normalizing the SI coefficient string fails.
235      */
236     private void addUnit(final Unit<U> unit) throws UnitException
237     {
238         if (!UNITS.containsKey(unit.getClass().getSimpleName()))
239         {
240             UNITS.put(unit.getClass().getSimpleName(), new HashSet<Unit<?>>());
241         }
242         UNITS.get(unit.getClass().getSimpleName()).add(unit);
243 
244         // resolve the SI coefficients, and normalize string
245         String siCoefficientsString = SICoefficients.normalize(getSICoefficientsString()).toString();
246         if (SI_COEFFICIENTS.containsKey(siCoefficientsString))
247         {
248             this.siCoefficients = SI_COEFFICIENTS.get(siCoefficientsString);
249         }
250         else
251         {
252             this.siCoefficients = new SICoefficients(SICoefficients.parse(siCoefficientsString));
253             SI_COEFFICIENTS.put(siCoefficientsString, this.siCoefficients);
254         }
255 
256         // add the standard unit
257         Map<Class<Unit<?>>, Unit<?>> unitMap = SI_UNITS.get(siCoefficientsString);
258         if (unitMap == null)
259         {
260             unitMap = new HashMap<Class<Unit<?>>, Unit<?>>();
261             SI_UNITS.put(siCoefficientsString, unitMap);
262         }
263         if (!unitMap.containsKey(unit.getClass()))
264         {
265             @SuppressWarnings("unchecked")
266             Class<Unit<?>> clazz = (Class<Unit<?>>) unit.getClass();
267             if (this.getStandardUnit() == null)
268             {
269                 unitMap.put(clazz, this);
270             }
271             else
272             {
273                 unitMap.put(clazz, this.getStandardUnit());
274             }
275         }
276     }
277 
278     /**
279      * Return a set of defined units for a given unit type.
280      * @param <V> the unit type to use in this method.
281      * @param unitClass Class&lt;V&gt;; the class for which the units are requested, e.g. ForceUnit.class
282      * @return the set of defined units belonging to the provided class. The empty set will be returned in case the unit type
283      *         does not have any units.
284      */
285     @SuppressWarnings("unchecked")
286     public static <V extends Unit<V>> Set<V> getUnits(final Class<V> unitClass)
287     {
288         if (!standardUnitsInitialized)
289         {
290             initializeStandardUnits();
291         }
292         Set<V> returnSet = new HashSet<V>();
293         if (UNITS.containsKey(unitClass.getSimpleName()))
294         {
295             for (Unit<?> unit : UNITS.get(unitClass.getSimpleName()))
296             {
297                 returnSet.add((V) unit);
298             }
299         }
300         return returnSet;
301     }
302 
303     /**
304      * Return a copy of the set of all defined units for this unit type.
305      * @return the set of defined units belonging to this Unit class. The empty set will be returned in case the unit type does
306      *         not have any units.
307      */
308     @SuppressWarnings("unchecked")
309     public final Set<Unit<U>> getAllUnitsOfThisType()
310     {
311         if (!standardUnitsInitialized)
312         {
313             initializeStandardUnits();
314         }
315         Set<Unit<U>> returnSet = new HashSet<Unit<U>>();
316         if (UNITS.containsKey(this.getClass().getSimpleName()))
317         {
318             for (Unit<?> unit : UNITS.get(this.getClass().getSimpleName()))
319             {
320                 returnSet.add((Unit<U>) unit);
321             }
322         }
323         return returnSet;
324     }
325 
326     /**
327      * @return name, e.g. meters per second
328      */
329     public final String getName()
330     {
331         if (this.name != null)
332         {
333             return this.name;
334         }
335         if (localization.isDefault())
336         {
337             return getDefaultLocaleName();
338         }
339         String[] loc = localization.getString(this.abbreviationKey).split("\\|");
340         if (loc.length >= 2)
341         {
342             return loc[1].trim();
343         }
344         if (loc.length >= 1)
345         {
346             return loc[0].trim();
347         }
348         return this.abbreviationKey;
349     }
350 
351     /**
352      * @return the name in the default locale, e.g. meters per second
353      */
354     public final String getDefaultLocaleName()
355     {
356         if (this.name != null)
357         {
358             return this.name;
359         }
360         if (this.cachedDefaultLocaleInfo.length >= 2)
361         {
362             return this.cachedDefaultLocaleInfo[1].trim();
363         }
364         if (this.cachedDefaultLocaleInfo.length >= 1)
365         {
366             return this.cachedDefaultLocaleInfo[0].trim();
367         }
368         return this.abbreviationKey;
369     }
370 
371     /**
372      * @return abbreviation, e.g., m/s
373      */
374     public final String getAbbreviation()
375     {
376         if (this.abbreviation != null)
377         {
378             return this.abbreviation;
379         }
380         if (localization.isDefault())
381         {
382             return getDefaultLocaleAbbreviation();
383         }
384         String[] loc = localization.getString(this.abbreviationKey).split("\\|");
385         if (loc.length >= 1)
386         {
387             return loc[0].trim();
388         }
389         return this.abbreviationKey;
390     }
391 
392     /**
393      * @return the abbreviation in the default locale, e.g., m/s
394      */
395     public final String getDefaultLocaleAbbreviation()
396     {
397         if (this.abbreviation != null)
398         {
399             return this.abbreviation;
400         }
401         if (this.cachedDefaultLocaleInfo.length >= 1)
402         {
403             return this.cachedDefaultLocaleInfo[0].trim();
404         }
405         return this.abbreviationKey;
406     }
407 
408     /**
409      * This method returns the abbreviation key, or null in case the abbreviation is hard coded.
410      * @return abbreviation key, e.g. DurationUnit.m/s, or null for a user-defined unit
411      */
412     public final String getAbbreviationKey()
413     {
414         return this.abbreviationKey;
415     }
416 
417     /**
418      * Return the textual display types of the unit. In case the list contains more than one abbreviation, the first one is the
419      * default one. In case none is available, the standard abbreviation is used. In case that one is also not available the
420      * abbreviation key is used. Note: the abbreviation itself is not necessarily a <b>textual</b> representation.
421      * @return the textual display types of the unit
422      */
423     public final List<String> getTextualRepresentations()
424     {
425         if (this.abbreviation != null)
426         {
427             return Arrays.asList(new String[] {this.abbreviation});
428         }
429         if (localization.isDefault())
430         {
431             return getDefaultLocaleTextualRepresentations();
432         }
433         String[] loc = localization.getString(this.abbreviationKey).split("\\|");
434         if (loc.length >= 3)
435         {
436             List<String> textList = new ArrayList<>();
437             for (int i = 2; i < loc.length; i++)
438             {
439                 textList.add(loc[i].trim());
440             }
441             return textList;
442         }
443         if (loc.length >= 1)
444         {
445             return Arrays.asList(new String[] {loc[0].trim()});
446         }
447         return Arrays.asList(new String[] {this.abbreviationKey});
448     }
449 
450     /**
451      * Return the textual display types of the unit in the default locale. In case the list contains more than one abbreviation,
452      * the first one is the default one. In case none is available, the standard abbreviation is used. In case that one is also
453      * not available the abbreviation key is used. Note: the abbreviation itself is not necessarily a <b>textual</b>
454      * representation.
455      * @return the textual display types of the unit in the default locale
456      */
457     public final List<String> getDefaultLocaleTextualRepresentations()
458     {
459         if (this.abbreviation != null)
460         {
461             return Arrays.asList(new String[] {this.abbreviation});
462         }
463         if (this.cachedDefaultLocaleInfo.length >= 3)
464         {
465             List<String> textList = new ArrayList<>();
466             for (int i = 2; i < this.cachedDefaultLocaleInfo.length; i++)
467             {
468                 textList.add(this.cachedDefaultLocaleInfo[i].trim());
469             }
470             return textList;
471         }
472         if (this.cachedDefaultLocaleInfo.length >= 1)
473         {
474             return Arrays.asList(new String[] {this.cachedDefaultLocaleInfo[0].trim()});
475         }
476         return Arrays.asList(new String[] {this.abbreviationKey});
477     }
478 
479     /**
480      * Return the default textual display representation of the unit. In case there are more textual representations, the first
481      * one is the default one. In case none is available, the standard abbreviation is used. In case that one is also not
482      * available the abbreviation key is used. Note: the abbreviation itself is not necessarily a <b>textual</b> representation.
483      * @return the default textual display representation of the unit
484      */
485     public final String getDefaultTextualRepresentation()
486     {
487         if (this.abbreviation != null)
488         {
489             return this.abbreviation;
490         }
491         if (localization.isDefault())
492         {
493             return getDefaultLocaleTextualRepresentation();
494         }
495         String[] loc = localization.getString(this.abbreviationKey).split("\\|");
496         if (loc.length >= 3)
497         {
498             return loc[2].trim();
499         }
500         if (loc.length >= 1)
501         {
502             return loc[0].trim();
503         }
504         return this.abbreviationKey;
505     }
506 
507     /**
508      * Return the default textual display representation of the unit in the default locale. In case there are more textual
509      * representations, the first one is the default one. In case none is available, the standard abbreviation is used. In case
510      * that one is also not available the abbreviation key is used. Note: the abbreviation itself is not necessarily a
511      * <b>textual</b> representation.
512      * @return the default textual display representation of the unit in the default locale
513      */
514     public final String getDefaultLocaleTextualRepresentation()
515     {
516         if (this.abbreviation != null)
517         {
518             return this.abbreviation;
519         }
520         if (this.cachedDefaultLocaleInfo.length >= 3)
521         {
522             return this.cachedDefaultLocaleInfo[2].trim();
523         }
524         if (this.cachedDefaultLocaleInfo.length >= 1)
525         {
526             return this.cachedDefaultLocaleInfo[0].trim();
527         }
528         return this.abbreviationKey;
529     }
530 
531     /**
532      * @return the scale to transform between this unit and the reference (e.g., SI) unit.
533      */
534     @SuppressWarnings("checkstyle:designforextension")
535     public Scale getScale()
536     {
537         return this.scale;
538     }
539 
540     /**
541      * @return unitSystem, e.g. SI or Imperial
542      */
543     public final UnitSystem getUnitSystem()
544     {
545         return this.unitSystem;
546     }
547 
548     /**
549      * @return the SI standard unit for this unit, or the de facto standard unit if SI is not available
550      */
551     public abstract U getStandardUnit();
552 
553     /**
554      * @return the SI standard coefficients for this unit, e.g., kgm/s2 or m-2s2A or m^-2.s^2.A or m^-2s^2A (not necessarily
555      *         normalized)
556      */
557     public abstract String getSICoefficientsString();
558 
559     /**
560      * @return the SI coefficients
561      */
562     public final SICoefficients getSICoefficients()
563     {
564         return this.siCoefficients;
565     }
566 
567     /**
568      * Determine whether this unit is the standard unit.
569      * @return boolean; whether this is the standard unit or not
570      */
571     public final boolean isBaseSIUnit()
572     {
573         return this.baseSIUnit;
574     }
575 
576     /**
577      * @param normalizedSICoefficientsString String; the normalized string (e.g., kg.m/s2) to look up
578      * @return a set with the Units belonging to this string, or an empty set when it does not exist
579      */
580     public static Set<Unit<?>> lookupUnitWithSICoefficients(final String normalizedSICoefficientsString)
581     {
582         if (!standardUnitsInitialized)
583         {
584             initializeStandardUnits();
585         }
586         if (SI_UNITS.containsKey(normalizedSICoefficientsString))
587         {
588             return new HashSet<Unit<?>>(SI_UNITS.get(normalizedSICoefficientsString).values());
589         }
590         return new HashSet<Unit<?>>();
591     }
592 
593     /**
594      * @param normalizedSICoefficientsString the normalized string (e.g., kg.m/s2) to look up
595      * @return a set of Units belonging to this string, or a set with a new unit when it does not yet exist
596      */
597     public static Set<Unit<?>> lookupOrCreateUnitWithSICoefficients(final String normalizedSICoefficientsString)
598     {
599         if (!standardUnitsInitialized)
600         {
601             initializeStandardUnits();
602         }
603         if (SI_UNITS.containsKey(normalizedSICoefficientsString))
604         {
605             return new HashSet<Unit<?>>(SI_UNITS.get(normalizedSICoefficientsString).values());
606         }
607         SIUnit unit = new SIUnit("SIUnit." + normalizedSICoefficientsString);
608         Set<Unit<?>> unitSet = new HashSet<Unit<?>>();
609         unitSet.add(unit);
610         return unitSet;
611     }
612 
613     /**
614      * @param normalizedSICoefficientsString String; the normalized string (e.g., kg.m/s2) to look up
615      * @return the Unit belonging to this string, or a new unit when it does not yet exist
616      */
617     public static SIUnit lookupOrCreateSIUnitWithSICoefficients(final String normalizedSICoefficientsString)
618     {
619         if (!standardUnitsInitialized)
620         {
621             initializeStandardUnits();
622         }
623         if (SI_UNITS.containsKey(normalizedSICoefficientsString)
624                 && SI_UNITS.get(normalizedSICoefficientsString).containsKey(SIUnit.class))
625         {
626             return (SIUnit) SI_UNITS.get(normalizedSICoefficientsString).get(SIUnit.class);
627         }
628         SIUnit unit = new SIUnit("SIUnit." + normalizedSICoefficientsString);
629         return unit;
630     }
631 
632     /** {@inheritDoc} */
633     @Override
634     public final String toString()
635     {
636         return getAbbreviation();
637     }
638 
639     /**
640      * Generate a hashCode for caching.
641      * @return a hashCode that is consistent with the equals() method.
642      */
643     public final int generateHashCode()
644     {
645         final int prime = 31;
646         int result = 1;
647         result = prime * result + ((this.abbreviation == null) ? 0 : this.abbreviation.hashCode());
648         result = prime * result + ((this.abbreviationKey == null) ? 0 : this.abbreviationKey.hashCode());
649         result = prime * result + ((this.name == null) ? 0 : this.name.hashCode());
650         return result;
651     }
652 
653     /** {@inheritDoc} */
654     @SuppressWarnings("checkstyle:designforextension")
655     @Override
656     public int hashCode()
657     {
658         return this.cachedHashCode;
659     }
660 
661     /** {@inheritDoc} */
662     @SuppressWarnings({"checkstyle:designforextension", "checkstyle:needbraces"})
663     @Override
664     public boolean equals(final Object obj)
665     {
666         if (this == obj)
667             return true;
668         if (obj == null)
669             return false;
670         if (getClass() != obj.getClass())
671             return false;
672         Unit<?> other = (Unit<?>) obj;
673         if (this.abbreviation == null)
674         {
675             if (other.abbreviation != null)
676                 return false;
677         }
678         else if (!this.abbreviation.equals(other.abbreviation))
679             return false;
680         if (this.abbreviationKey == null)
681         {
682             if (other.abbreviationKey != null)
683                 return false;
684         }
685         else if (!this.abbreviationKey.equals(other.abbreviationKey))
686             return false;
687         if (this.name == null)
688         {
689             if (other.name != null)
690                 return false;
691         }
692         else if (!this.name.equals(other.name))
693             return false;
694         return true;
695     }
696 
697     /**
698      * Test if two units are the same, except for the name and abbreviation. This means for instance that for the MassUnits
699      * METRIC_TON and MEGAGRAM (both 1000 kg) equals(...) will yield false, but equalsIgnoreNaming will yield true.
700      * @param obj Object; the object to compare with
701      * @return true if the two units are the same numerically, except for the naming and/or abbreviation
702      */
703     public abstract boolean equalsIgnoreNaming(Object obj);
704 
705 }