• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

unitsofmeasurement / indriya / 2297

19 Feb 2025 09:43PM UTC coverage: 71.129% (-0.08%) from 71.208%
2297

push

circleci

keilw
435: AbstractUnit.asType() will accept any si-units as valid class input

Task-Url: https://github.com/unitsofmeasurement/indriya/issues/435

1 of 1 new or added line in 1 file covered. (100.0%)

5 existing lines in 1 file now uncovered.

3730 of 5244 relevant lines covered (71.13%)

0.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.33
/src/main/java/tech/units/indriya/AbstractUnit.java
1
/*
2
 * Units of Measurement Reference Implementation
3
 * Copyright (c) 2005-2025, Jean-Marie Dautelle, Werner Keil, Otavio Santana.
4
 *
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without modification,
8
 * are permitted provided that the following conditions are met:
9
 *
10
 * 1. Redistributions of source code must retain the above copyright notice,
11
 *    this list of conditions and the following disclaimer.
12
 *
13
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
14
 *    and the following disclaimer in the documentation and/or other materials provided with the distribution.
15
 *
16
 * 3. Neither the name of JSR-385, Indriya nor the names of their contributors may be used to endorse or promote products
17
 *    derived from this software without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
 */
30
package tech.units.indriya;
31

32
import static javax.measure.Quantity.Scale.ABSOLUTE;
33
import static org.apiguardian.api.API.Status.EXPERIMENTAL;
34

35
import java.io.Serializable;
36
import java.lang.reflect.ParameterizedType;
37
import java.lang.reflect.Type;
38
import java.util.HashMap;
39
import java.util.Map;
40

41
import javax.measure.Dimension;
42
import javax.measure.IncommensurableException;
43
import javax.measure.Prefix;
44
import javax.measure.Quantity;
45
import javax.measure.UnconvertibleException;
46
import javax.measure.Unit;
47
import javax.measure.UnitConverter;
48
import javax.measure.Quantity.Scale;
49
import javax.measure.format.MeasurementParseException;
50
import javax.measure.quantity.Dimensionless;
51
import javax.measure.spi.SystemOfUnits;
52

53
import org.apiguardian.api.API;
54

55
import tech.units.indriya.format.LocalUnitFormat;
56
import tech.units.indriya.format.SimpleUnitFormat;
57
import tech.units.indriya.function.AbstractConverter;
58
import tech.units.indriya.function.AddConverter;
59
import tech.units.indriya.function.Calculus;
60
import tech.units.indriya.function.MultiplyConverter;
61
import tech.units.indriya.function.RationalNumber;
62
import tech.units.indriya.internal.function.Calculator;
63
import tech.units.indriya.spi.DimensionalModel;
64
import tech.units.indriya.unit.AlternateUnit;
65
import tech.units.indriya.unit.AnnotatedUnit;
66
import tech.units.indriya.unit.ProductUnit;
67
import tech.units.indriya.unit.TransformedUnit;
68
import tech.units.indriya.unit.UnitDimension;
69
import tech.units.indriya.unit.Units;
70
import tech.uom.lib.common.function.Nameable;
71
import tech.uom.lib.common.function.PrefixOperator;
72
import tech.uom.lib.common.function.SymbolSupplier;
73

74
/**
75
 * <p>
76
 * The class represents units founded on the seven <b>SI</b> base units for
77
 * seven base quantities assumed to be mutually independent.
78
 * </p>
79
 *
80
 * <p>
81
 * For all physics units, unit conversions are symmetrical:
82
 * <code>u1.getConverterTo(u2).equals(u2.getConverterTo(u1).inverse())</code>.
83
 * Non-physical units (e.g. currency units) for which conversion is not
84
 * symmetrical should have their own separate class hierarchy and are considered
85
 * distinct (e.g. financial units), although they can always be combined with
86
 * physics units (e.g. "€/Kg", "$/h").
87
 * </p>
88
 *
89
 * @see <a href=
90
 *      "http://en.wikipedia.org/wiki/International_System_of_Units">Wikipedia:
91
 *      International System of Units</a>
92
 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
93
 * @author <a href="mailto:werner@units.tech">Werner Keil</a>
94
 * @version 4.2, February 19, 2025
95
 * @since 1.0
96
 */
97
public abstract class AbstractUnit<Q extends Quantity<Q>>
98
                implements Unit<Q>, Comparable<Unit<Q>>, PrefixOperator<Q>, Nameable, Serializable, SymbolSupplier {
99

100
        /**
101
         * 
102
         */
103
        private static final long serialVersionUID = -4344589505537030204L;
104

105
        /**
106
         * Holds the dimensionless unit <code>ONE</code>.
107
         * 
108
         * @see <a href=
109
         *      "https://en.wikipedia.org/wiki/Natural_units#Choosing_constants_to_normalize">
110
         *      Wikipedia: Natural Units - Choosing constants to normalize</a>
111
         * @see <a href= "http://www.av8n.com/physics/dimensionless-units.htm">Units of
112
         *      Dimension One</a>
113
         */
114
        public static final Unit<Dimensionless> ONE = new ProductUnit<>();
1✔
115

116
        /**
117
         * Holds the name.
118
         */
119
        private String name;
120

121
        /**
122
         * Holds the symbol.
123
         */
124
        private String symbol;
125
        
126
    /**
127
     * Holds the measurement scale
128
     */
129
        protected Scale scale = ABSOLUTE;
1✔
130

131
        /**
132
         * Holds the unique symbols collection (base units or alternate units).
133
         */
134
        protected static final transient Map<String, Unit<?>> SYMBOL_TO_UNIT = new HashMap<>();
1✔
135

136
        /**
137
         * Default constructor.
138
         */
139
        protected AbstractUnit() {
1✔
140
        }
1✔
141

142
        /**
143
         * Constructor setting a symbol.
144
         * 
145
         * @param symbol the unit symbol.
146
         */
147
        protected AbstractUnit(String symbol) {
1✔
148
                this.symbol = symbol;
1✔
149
        }
1✔
150

151
        protected Type getActualType() {
152
                ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
1✔
153
                return parameterizedType.getActualTypeArguments()[0].getClass().getGenericInterfaces()[0];
1✔
154
        }
155

156
        /**
157
         * Indicates if this unit belongs to the set of coherent SI units (unscaled SI
158
         * units).
159
         * 
160
         * The base and coherent derived units of the SI form a coherent set, designated
161
         * the set of coherent SI units. The word coherent is used here in the following
162
         * sense: when coherent units are used, equations between the numerical values
163
         * of quantities take exactly the same form as the equations between the
164
         * quantities themselves. Thus if only units from a coherent set are used,
165
         * conversion factors between units are never required.
166
         * 
167
         * @return <code>equals(toSystemUnit())</code>
168
         */
169
        public boolean isSystemUnit() {
170
                Unit<Q> sys = this.toSystemUnit();
1✔
171
                return this == sys || this.equals(sys);
1✔
172
        }
173
        
174
        /**
175
         * Returns the converter from this unit to its unscaled {@link #toSystemUnit
176
         * System Unit} unit.
177
         *
178
         * @return <code>getConverterTo(this.toSystemUnit())</code>
179
         * @see #toSystemUnit
180
         */
181
        public abstract UnitConverter getSystemConverter();
182
        
183
        /**
184
         * Returns the unscaled {@link SI} unit from which this unit is derived.
185
         * 
186
         * The SI unit can be be used to identify a quantity given the unit. For
187
         * example:<code> static boolean isAngularVelocity(AbstractUnit<?> unit) {
188
         * return unit.toSystemUnit().equals(RADIAN.divide(SECOND)); } assert(REVOLUTION.divide(MINUTE).isAngularVelocity()); // Returns true. </code>
189
         *
190
         * @return the unscaled metric unit from which this unit is derived.
191
         */
192
        protected abstract Unit<Q> toSystemUnit();
193

194
        /**
195
         * Annotates the specified unit. Annotation does not change the unit semantic.
196
         * Annotations are often written between curly braces behind units. For
197
         * example:<br>
198
         * <code> Unit<Volume> PERCENT_VOL = ((AbstractUnit)Units.PERCENT).annotate("vol"); // "%{vol}" Unit<Mass> KG_TOTAL =
199
         * ((AbstractUnit)Units.KILOGRAM).annotate("total"); // "kg{total}" Unit<Dimensionless> RED_BLOOD_CELLS = ((AbstractUnit)Units.ONE).annotate("RBC"); // "{RBC}" </code>
200
         *
201
         * Note: Annotation of system units are not considered themselves as system
202
         * units.
203
         *
204
         * @param annotation the unit annotation.
205
         * @return the annotated unit.
206
         */
207
    public final Unit<Q> annotate(String annotation) {
208
      return new AnnotatedUnit<>(this, annotation);
1✔
209
    }
210

211
        /**
212
         * Returns the abstract unit represented by the specified characters as per
213
         * default format.
214
         *
215
         * Locale-sensitive unit parsing could be handled using {@link LocalUnitFormat}
216
         * in subclasses of AbstractUnit.
217
         *
218
         * <p>
219
         * Note: The standard format supports dimensionless
220
         * units.<code> AbstractUnit<Dimensionless> PERCENT =
221
         * AbstractUnit.parse("100").inverse().asType(Dimensionless.class); </code>
222
         * </p>
223
         *
224
         * @param charSequence the character sequence to parse.
225
         * @return <code>SimpleUnitFormat.getInstance().parse(csq)</code>
226
         * @throws MeasurementParseException if the specified character sequence cannot
227
         *                                   be correctly parsed (e.g. not UCUM
228
         *                                   compliant).
229
         */
230
        public static Unit<?> parse(CharSequence charSequence) {
231
                return SimpleUnitFormat.getInstance().parse(charSequence);
1✔
232
        }
233

234
        /**
235
         * Returns the standard representation of this physics unit. The string produced
236
         * for a given unit is always the same; it is not affected by the locale. It can
237
         * be used as a canonical string representation for exchanging units, or as a
238
         * key for a Hashtable, etc.
239
         *
240
         * Locale-sensitive unit parsing could be handled using {@link LocalUnitFormat}
241
         * in subclasses of AbstractUnit.
242
         *
243
         * @return <code>SimpleUnitFormat.getInstance().format(this)</code>
244
         */
245
        @Override
246
        public String toString() {
247
                return SimpleUnitFormat.getInstance().format(this);
1✔
248
        }
249

250
        // ///////////////////////////////////////////////////////
251
        // Implements javax.measure.Unit<Q> interface //
252
        // ///////////////////////////////////////////////////////
253

254
        /**
255
         * Returns the system unit (unscaled SI unit) from which this unit is derived.
256
         * They can be be used to identify a quantity given the unit. For example:<br>
257
         * <code> static boolean isAngularVelocity(AbstractUnit<?> unit) {<br>&nbsp;&nbsp;return unit.getSystemUnit().equals(RADIAN.divide(SECOND));<br>}
258
         * <br>assert(REVOLUTION.divide(MINUTE).isAngularVelocity()); // Returns true. </code>
259
         *
260
         * @return the unscaled metric unit from which this unit is derived.
261
         */
262
        @Override
263
        public final Unit<Q> getSystemUnit() {
264
                return toSystemUnit();
1✔
265
        }
266

267
        /**
268
         * Indicates if this unit is compatible with the unit specified. To be
269
         * compatible both units must be physics units having the same fundamental
270
         * dimension.
271
         *
272
         * @param that the other unit.
273
         * @return <code>true</code> if this unit and that unit have the same
274
         *         fundamental dimension according to the current dimensional model;
275
         *         <code>false</code> otherwise.
276
         */
277
        @Override
278
        public final boolean isCompatible(Unit<?> that) {
279
                return internalIsCompatible(that, true);
1✔
280
        }
281

282
        /**
283
         * Casts this unit to a parameterized unit of specified nature or throw a
284
         * ClassCastException if the dimension of the specified quantity and this unit's
285
         * dimension do not match (regardless whether or not the dimensions are
286
         * independent or not).
287
         *
288
         * @param type the quantity class identifying the nature of the unit.
289
         * @throws ClassCastException if the dimension of this unit is different from
290
         *                            the SI dimension of the specified type.
291
         * @see Units#getUnit(Class)
292
         */
293
        @SuppressWarnings("unchecked")
294
        @Override
295
        public final <T extends Quantity<T>> Unit<T> asType(Class<T> type) {
296
                //return asType(type, Units.getInstance());
297
                
298
                Dimension typeDimension = UnitDimension.of(type);
1✔
299
                if (typeDimension != null && !typeDimension.equals(this.getDimension()))
1✔
300
                        throw new ClassCastException("The unit: " + this + " is not compatible with quantities of type " + type);
1✔
301
                return (Unit<T>) this;                
1✔
302
        }
303

304
        @Override
305
        public abstract Map<? extends Unit<?>, Integer> getBaseUnits();
306

307
        @Override
308
        public abstract Dimension getDimension();
309

310
        protected void setName(String name) {
311
                this.name = name;
1✔
312
        }
1✔
313

314
        public String getName() {
315
                return name;
1✔
316
        }
317

318
        public String getSymbol() {
319
                return symbol;
1✔
320
        }
321

322
        protected void setSymbol(String s) {
323
                this.symbol = s;
1✔
324
        }
1✔
325

326
        @Override
327
        public final UnitConverter getConverterTo(Unit<Q> that) throws UnconvertibleException {
328
                return internalGetConverterTo(that, true);
1✔
329
        }
330

331
        @Override
332
        public final UnitConverter getConverterToAny(Unit<?> that) throws IncommensurableException, UnconvertibleException {
333
                return getConverterToAny(that, ABSOLUTE);
1✔
334
        }
335
        
336
        @SuppressWarnings("unchecked")
337
        public final <T extends Quantity<T>> Unit<T> asType(Class<T> type, SystemOfUnits typeSystem) {
UNCOV
338
                Unit<T> typedUnit = typeSystem.getUnit(type); 
×
UNCOV
339
                final Dimension typeDimension = (typedUnit != null ? typedUnit.getDimension() : null);
×
UNCOV
340
                if (typeDimension != null && !typeDimension.equals(this.getDimension()))
×
UNCOV
341
                        throw new ClassCastException("The unit: " + this + " is not compatible with quantities of type " + type);
×
UNCOV
342
                return (Unit<T>) this;
×
343
        }
344
        
345
   /**
346
     * Returns a converter of numeric values from this unit to another unit of same type. This method performs the same work as
347
     * {@link #getConverterToAny(Unit)} without raising checked exception.
348
     *
349
     * @param that
350
     *          the unit of same type to which to convert the numeric values.
351
     * @param scale the measurement scale.          
352
     * @return the converter from this unit to {@code that} unit in the given {@code scale}.
353
     * @throws UnconvertibleException
354
     *           if a converter cannot be constructed.
355
     *
356
     * @see #getConverterToAny(Unit)
357
     */
358
        @API(status=EXPERIMENTAL)
359
        public final UnitConverter getConverterTo(Unit<Q> that, Scale scale) throws UnconvertibleException {
360
                this.scale = scale;
×
361
                return getConverterTo(that);
×
362
        }
363
        
364
        /**
365
     * Returns a converter from this unit to the specified unit of type unknown in the given scale. This method can be used when the quantity type of the specified unit is
366
     * unknown at compile-time or when dimensional analysis allows for conversion between units of different type.
367
     *
368
     * <p>
369
     * To convert to a unit having the same parameterized type, {@link #getConverterTo(Unit, Scale)} is preferred (no checked exception raised).
370
     * </p>
371
     *
372
     * @param that
373
     *          the unit to which to convert the numeric values.
374
     * @param scale the measurement scale.
375
     * @return the converter from {@code this} unit to {@code that} unit using the given {@code scale}.
376
     * @throws IncommensurableException
377
     *           if this unit is not {@linkplain #isCompatible(Unit) compatible} with {@code that}.
378
     * @throws UnconvertibleException
379
     *           if a converter cannot be constructed.
380
     *
381
     * @see #getConverterTo(Unit)
382
     * @see #isCompatible(Unit)
383
     */
384
        @API(status=EXPERIMENTAL)
385
        @SuppressWarnings("rawtypes")
386
        public final UnitConverter getConverterToAny(Unit<?> that, Scale scale) throws IncommensurableException, UnconvertibleException {
387
                if (!isCompatible(that))
1✔
388
                        throw new IncommensurableException(this + " is not compatible with " + that);
1✔
389
                this.scale = scale;
1✔
390
                final AbstractUnit thatAbstr = (AbstractUnit) that; // Since both units are
1✔
391
                // compatible they must both be abstract units.
392
                final DimensionalModel model = DimensionalModel.current();
1✔
393
                Unit thisSystemUnit = this.getSystemUnit();
1✔
394
                UnitConverter thisToDimension = model.getDimensionalTransform(thisSystemUnit.getDimension())
1✔
395
                                .concatenate(this.getSystemConverter());
1✔
396
                Unit thatSystemUnit = thatAbstr.getSystemUnit();
1✔
397
                UnitConverter thatToDimension = model.getDimensionalTransform(thatSystemUnit.getDimension())
1✔
398
                                .concatenate(thatAbstr.getSystemConverter());
1✔
399
                return thatToDimension.inverse().concatenate(thisToDimension);
1✔
400
        }
401

402
        @Override
403
        public final Unit<Q> alternate(String newSymbol) {
404
                return new AlternateUnit<>(this, newSymbol);
1✔
405
        }
406

407
        @Override
408
        public final Unit<Q> transform(UnitConverter operation) {
409
                Unit<Q> systemUnit = this.getSystemUnit();
1✔
410
                UnitConverter cvtr;
411
                if (this.isSystemUnit()) {
1✔
412
                        cvtr = this.getSystemConverter().concatenate(operation);
1✔
413
                } else {
414
                        cvtr = operation;
1✔
415
                }
416
                return cvtr.isIdentity() ? systemUnit : new TransformedUnit<>(null, this, systemUnit, cvtr);
1✔
417
        }
418

419
        @Override
420
        public final Unit<Q> shift(Number offset) {
421
                if (Calculus.currentNumberSystem().isZero(offset))
1✔
422
                        return this;
×
423
                return transform(new AddConverter(offset));
1✔
424
        }
425

426
        @Override
427
        public final Unit<Q> multiply(Number factor) {
428
                if (Calculus.currentNumberSystem().isOne(factor))
1✔
429
                        return this;
1✔
430
                return transform(MultiplyConverter.of(factor));
1✔
431
        }
432

433
        @Override
434
        public Unit<Q> shift(double offset) {
435
                return shift(RationalNumber.of(offset));
1✔
436
        }
437

438
        @Override
439
        public Unit<Q> multiply(double multiplier) {
440
                return multiply(RationalNumber.of(multiplier));
1✔
441
        }
442

443
        @Override
444
        public Unit<Q> divide(double divisor) {
445
                return divide(RationalNumber.of(divisor));
1✔
446
        }
447
        
448
        /**
449
         * Internal helper for isCompatible
450
         */
451
        private final boolean internalIsCompatible(Unit<?> that, boolean checkEquals) {
452
                if (checkEquals) {
1✔
453
                        if (this == that || this.equals(that))
1✔
454
                                return true;
1✔
455
                } else {
456
                        if (this == that)
×
457
                                return true;
×
458
                }
459
                if (!(that instanceof Unit))
1✔
460
                        return false;
×
461
                Dimension thisDimension = this.getDimension();
1✔
462
                Dimension thatDimension = that.getDimension();
1✔
463
                if (thisDimension.equals(thatDimension))
1✔
464
                        return true;
1✔
465
                DimensionalModel model = DimensionalModel.current(); // Use
1✔
466
                // dimensional
467
                // analysis
468
                // model.
469
                return model.getFundamentalDimension(thisDimension).equals(model.getFundamentalDimension(thatDimension));
1✔
470
        }
471

472
        protected final UnitConverter internalGetConverterTo(Unit<Q> that, boolean useEquals)
473
                        throws UnconvertibleException {
474
                if (useEquals) {
1✔
475
                        if (this == that || this.equals(that))
1✔
476
                                return AbstractConverter.IDENTITY;
1✔
477
                } else {
478
                        if (this == that)
×
479
                                return AbstractConverter.IDENTITY;
×
480
                }
481
                Unit<Q> thisSystemUnit = this.getSystemUnit();
1✔
482
                Unit<Q> thatSystemUnit = that.getSystemUnit();
1✔
483
                if (!thisSystemUnit.equals(thatSystemUnit))
1✔
484
                        try {
485
                                return getConverterToAny(that);
1✔
486
                        } catch (IncommensurableException e) {
1✔
487
                                throw new UnconvertibleException(e);
1✔
488
                        }
489
                UnitConverter thisToSI = this.getSystemConverter();
1✔
490
                UnitConverter thatToSI = that.getConverterTo(thatSystemUnit);
1✔
491
                return thatToSI.inverse().concatenate(thisToSI);
1✔
492
        }
493

494
        /**
495
         * Returns the product of this physical unit with the one specified.
496
         *
497
         * @param that the physical unit multiplicand.
498
         * @return <code>this * that</code>
499
         */
500
        public final Unit<?> multiply(Unit<?> that) {
501
                if (this.equals(ONE))
1✔
502
                        return that;
1✔
503
                if (that.equals(ONE))
1✔
504
                        return this;
1✔
505
                return ProductUnit.ofProduct(this, that);
1✔
506
        }
507

508
        /**
509
         * Returns the inverse of this physical unit.
510
         *
511
         * @return <code>1 / this</code>
512
         */
513
        @Override
514
        public final Unit<?> inverse() {
515
                if (this.equals(ONE))
1✔
516
                        return this;
1✔
517
                return ProductUnit.ofQuotient(ONE, this);
1✔
518
        }
519

520
        /**
521
         * Returns the result of dividing this unit by the specified divisor. If the
522
         * factor is an integer value, the division is exact. For example:
523
         * 
524
         * <pre>
525
         * <code>
526
         *    QUART = GALLON_LIQUID_US.divide(4); // Exact definition.
527
         * </code>
528
         * </pre>
529
         * 
530
         * @param divisor the divisor value.
531
         * @return this unit divided by the specified divisor.
532
         */
533
        @Override
534
        public final Unit<Q> divide(Number divisor) {
535
            if (Calculus.currentNumberSystem().isOne(divisor))
1✔
536
                        return this;
×
537
            Number factor = Calculator.of(divisor).reciprocal().peek(); 
1✔
538
                return transform(MultiplyConverter.of(factor));
1✔
539
        }
540

541
        /**
542
         * Returns the quotient of this unit with the one specified.
543
         *
544
         * @param that the unit divisor.
545
         * @return <code>this.multiply(that.inverse())</code>
546
         */
547
        @Override
548
        public final Unit<?> divide(Unit<?> that) {
549
                return this.multiply(that.inverse());
1✔
550
        }
551

552
        /**
553
         * Returns a unit equals to the given root of this unit.
554
         *
555
         * @param n the root's order.
556
         * @return the result of taking the given root of this unit.
557
         * @throws ArithmeticException if <code>n == 0</code> or if this operation would
558
         *                             result in an unit with a fractional exponent.
559
         */
560
        @Override
561
        public final Unit<?> root(int n) {
562
                if (n > 0)
1✔
563
                        return ProductUnit.ofRoot(this, n);
1✔
564
                else if (n == 0)
×
565
                        throw new ArithmeticException("Root's order of zero");
×
566
                else
567
                        // n < 0
568
                        return ONE.divide(this.root(-n));
×
569
        }
570

571
        /**
572
         * Returns a unit equals to this unit raised to an exponent.
573
         *
574
         * @param n the exponent.
575
         * @return the result of raising this unit to the exponent.
576
         */
577
        @Override
578
        public Unit<?> pow(int n) {
579
                if (n > 0)
1✔
580
                        return this.multiply(this.pow(n - 1));
1✔
581
                else if (n == 0)
1✔
582
                        return ONE;
1✔
583
                else
584
                        // n < 0
585
                        return ONE.divide(this.pow(-n));
1✔
586
        }
587

588
        @Override
589
        public Unit<Q> prefix(Prefix prefix) {
590
                return this.transform(MultiplyConverter.ofPrefix(prefix));
1✔
591
        }
592
        
593
        /**
594
         * Compares this unit to the specified unit. The default implementation compares
595
         * the name and symbol of both this unit and the specified unit, giving
596
         * precedence to the symbol.
597
         *
598
         * @return a negative integer, zero, or a positive integer as this unit is less
599
         *         than, equal to, or greater than the specified unit.
600
         */
601
        @Override
602
        public int compareTo(Unit<Q> that) {
603
                int symbolComparison = compareToWithPossibleNullValues(getSymbol(), that.getSymbol());
1✔
604
                if (symbolComparison == 0) {
1✔
605
                        return compareToWithPossibleNullValues(name, that.getName());
1✔
606
                } else {
607
                        return symbolComparison;
1✔
608
                }
609
        }
610

611
        private int compareToWithPossibleNullValues(String a, String b) {
612
                if (a == null) {
1✔
613
                        return (b == null) ? 0 : -1;
1✔
614
                } else {
615
                        return (b == null) ? 1 : a.compareTo(b);
1✔
616
                }
617
        }
618

619
        @Override
620
        public boolean isEquivalentTo(Unit<Q> that) {
621
                return this.getConverterTo(that).isIdentity();
1✔
622
        }
623

624
        // //////////////////////////////////////////////////////////////
625
        // Ensures that sub-classes implement the hashCode method.
626
        // //////////////////////////////////////////////////////////////
627

628
        @Override
629
        public abstract boolean equals(Object obj);
630

631
        @Override
632
        public abstract int hashCode();
633

634
        /**
635
         * Utility class for number comparison and equality
636
         */
637
        protected static final class Equalizer {
×
638
                /**
639
                 * Indicates if this unit is considered equals to the specified object. order).
640
                 *
641
                 * @param obj the object to compare for equality.
642
                 * @return <code>true</code> if <code>this</code> and <code>obj</code> are
643
                 *         considered equal; <code>false</code>otherwise.
644
                 */
645
                public static boolean areEqual(@SuppressWarnings("rawtypes") Unit u1,
646
                                @SuppressWarnings("rawtypes") Unit u2) {
647
                        /*
648
                         * if (u1 != null && u2 != null) { if (u1.getName() != null && u1.getSymbol() !=
649
                         * null) { return u1.getName().equals(u2.getName()) &&
650
                         * u1.getSymbol().equals(u2.getSymbol()) && u1.internalIsCompatible(u2, false);
651
                         * } else if (u1.getSymbol() != null) { return
652
                         * u1.getSymbol().equals(u2.getSymbol()) && u1.internalIsCompatible(u2, false);
653
                         * } else { return u1.toString().equals(u2.toString()) &&
654
                         * u1.internalIsCompatible(u2, false); } } else {
655
                         */
656
                        if (u1 != null && u1.equals(u2))
×
657
                                return true;
×
658
                        return false;
×
659
                }
660
        }
661
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc