Version 4.5.0

hirondelle.web4j.model
Class Decimal

Object
  extended by Number
      extended by hirondelle.web4j.model.Decimal
All Implemented Interfaces:
Serializable, Comparable<Decimal>

public final class Decimal
extends Number
implements Comparable<Decimal>, Serializable

Represents a decimal amount.

Decimal amounts are typically used to represent two kinds of items :

Your applications are not obliged to use this class to represent decimal amounts. You may choose to use BigDecimal instead (perhaps along with an Id to store a currency, if needed).

This class exists for these reasons :

Decimal objects are immutable. Many operations return new Decimal objects.

Currency Is Unspecified

This class can be used to model amounts of money.

Many will be surprised that this class does not make any reference to currency. The reason for this is adding currency would render this class a poor building block. Building block objects such as Date, Integer, and so on, are atomic, in the sense of representing a single piece of data. They correspond to a single column in a table, or a single form control. If the currency were included in this class, then it would no longer be atomic, and it could not be treated by WEB4J as any other building block class. However, allowing this class to be treated like any other building block class is highly advantageous.

If a feature needs to explicitly distinguish between multiple currencies such as US Dollars and Japanese Yen, then a Decimal object will need to be paired by the caller with a second item representing the underlying currency (perhaps modeled as an Id). See the Currency class for more information.

Number of Decimal Places

To validate the number of decimals in your Model Objects, call the Check.numDecimalsAlways(int) or Check.numDecimalsMax(int) methods.

The init(RoundingMode, int) method is called upon startup. It takes a parameter which specifies the number of decimal places. It is used only for rounding the results of times and div operations. It is not used to validate the number of decimals in items passed to the Decimal constructor.

Different Numbers of Decimals

As usual, operations can be performed on two items having a different number of decimal places. For example, these operations are valid (using an informal, ad hoc notation) :

10 + 1.23 = 11.23
10.00 + 1.23 = 11.23
10 - 1.23 = 8.77
(10 > 1.23) => true 
This corresponds to typical user expectations.

Note that equals(Object) is unusual in that it is the only method sensitive to the exact number of decimal places, while eq(Decimal) is not. That is,

10.equals(10.00) => false
10.eq(10.00) => true

Results With 'Extra' Decimal Places

The times and div operations are different, since the result can have a larger number of decimals than usual. For example, when dealing with US Dollars, the result

$10.00 x 0.1256 = $1.256
has more than two decimals. In such cases, this class will round results of multiplication and division, using the setting passed to init(RoundingMode, int). This policy likely conforms to the expectations of most end users. The times(long) method is an exception to this rule.

The init(RoundingMode, int) method takes two parameters. One controls the rounding policy, and the other controls the number of decimals to use by default. When the default number of decimals needs to be overridden, you must use the changeTimesDivDecimals(int) method.

Terse Method Names

Various methods in this class have unusually terse names, such as lt for 'less than', and gt for 'greater than', and so on. The intent of such names is to improve the legibility of mathematical expressions.

Example :

 if ( amount.lt(hundred) ) {
     cost = amount.times(price); 
 }

Prefer Decimal to Double

The times and div methods are overloaded to take int for round numbers, and Decimal or double for numbers with a decimal.

In short, the double versions are best suited when using hard-coded values for factors and divisors, while the Decimal versions are suited for the (more common) case of using values coming from the database or user input.

Using Decimal is the preferred form, since there are many pitfalls associated with double. The double form has been retained since it's more convenient for the caller in some cases, and one of the goals of this class is to allow terse mathematical expressions.

Extends Number

This class extends Number. An immediate benefit of this is that it allows JSTL's fmt tags to render Decimal objects in the usual way.

See Also:
Serialized Form

Field Summary
static Decimal ZERO
          Zero Decimal amount.
 
Constructor Summary
Decimal(BigDecimal aAmount)
          Full constructor.
 
Method Summary
 Decimal abs()
          Return the absolute value of the amount.
 Decimal changeTimesDivDecimals(int aTimesDivDecimals)
          Override the default number of decimals retained in times and div operations.
 int compareTo(Decimal aThat)
          Implements the Comparable interface.
 Decimal div(Decimal aDivisor)
          Divide this Decimal by an non-integral divisor.
 Decimal div(double aDivisor)
          Divide this Decimal by an non-integral divisor.
 Decimal div(long aDivisor)
          Divide this Decimal by an integral divisor.
 double doubleValue()
          Required by Number.
 boolean eq(Decimal aThat)
          Equals (insensitive to number of decimals).
 boolean equals(Object aThat)
          Equals (sensitive to number of decimals).
 float floatValue()
          Required by Number.
static Decimal from(String aAmount)
          Convenience factory method.
 BigDecimal getAmount()
          Return the amount passed to the constructor.
 int getNumDecimals()
          Return the number of decimals in this value.
static RoundingMode getRoundingStyle()
          Return the rounding style passed to the init method.
 int getTimesDivDecimals()
          Return the number of decimals to be retained by times and div operations.
static int getTimesDivDecimalsDefault()
          Return the number of decimals passed to the init method.
 boolean gt(Decimal aThat)
          Greater than.
 boolean gteq(Decimal aThat)
          Greater than or equal to.
 int hashCode()
           
static void init(RoundingMode aRounding, int aNumDecimalsForTimesDiv)
          Set default values for the rounding style, and the maximum number of decimals to use when calculating results of times and div operations.
 int intValue()
          Required by Number.
 boolean isMinus()
          Return true only if the amount is negative.
 boolean isPlus()
          Return true only if the amount is positive.
 boolean isZero()
          Return true only if the amount is zero.
 long longValue()
          Required by Number.
 boolean lt(Decimal aThat)
          Less than.
 boolean lteq(Decimal aThat)
          Less than or equal to.
 Decimal minus(Decimal aThat)
          Subtract aThat Decimal from this Decimal.
 Decimal negate()
          Return the amount x (-1).
 Decimal plus(Decimal aThat)
          Add aThat Decimal to this Decimal.
 Decimal round()
          Round to an integer.
 Decimal round(int aNumberOfDecimals)
          Round to 0 or more decimal places.
static Decimal sum(Collection<Decimal> aDecimals)
          Sum a collection of Decimal objects.
 Decimal times(Decimal aFactor)
          Multiply this Decimal by an non-integral factor (having a decimal point).
 Decimal times(double aFactor)
          Multiply this Decimal by an non-integral factor (having a decimal point).
 Decimal times(long aFactor)
          Multiply this Decimal by an integral factor.
 String toString()
          Renders this Decimal in a style suitable for debugging.
 
Methods inherited from class Number
byteValue, shortValue
 
Methods inherited from class Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
 

Field Detail

ZERO

public static final Decimal ZERO
Zero Decimal amount.

Like BigDecimal.ZERO, this item has no explicit decimal. In most cases that will not matter, since only the equals(Object) method is sensitive to exact decimals. All other methods, including eq(Decimal), are not sensitive to exact decimals.

Constructor Detail

Decimal

public Decimal(BigDecimal aAmount)
Full constructor.

Parameters:
aAmount - required, can be positive or negative. Any number of decimals. The value of BigDecimal.scale() cannot be negative.
Method Detail

init

public static void init(RoundingMode aRounding,
                        int aNumDecimalsForTimesDiv)
Set default values for the rounding style, and the maximum number of decimals to use when calculating results of times and div operations.

This method is called by the framework upon startup. The recommended rounding style is RoundingMode.HALF_EVEN, also called banker's rounding. That rounding style introduces the least bias.

Parameters:
aRounding - defines how all numbers are rounded by this class. This rounding style is set once, and cannot be overridden for individual Decimal objects.
aNumDecimalsForTimesDiv - number of decimals for results of times and div operations. Must be 0 or more. Taking the example of US Dollars, this setting would usually be '2'.

getRoundingStyle

public static RoundingMode getRoundingStyle()
Return the rounding style passed to the init method.


getTimesDivDecimalsDefault

public static int getTimesDivDecimalsDefault()
Return the number of decimals passed to the init method.


from

public static Decimal from(String aAmount)
Convenience factory method.

Instead of :

Decimal decimal = new Decimal(new BigDecimal("100"));
one may instead use :
Decimal decimal = Decimal.from("100");
which is a bit more legible. This is especially useful when you need to define specific minimum and maximum values used in validation.


changeTimesDivDecimals

public Decimal changeTimesDivDecimals(int aTimesDivDecimals)
Override the default number of decimals retained in times and div operations. The default number of decimals retained in times and div operations is set during the call to init(RoundingMode, int). The changeTimesDivDecimals method is called when that default is not appropriate.

This method will often be used when modeling physical measurements such as temperature, distance, and so on, where the number of decimals can vary according to context.


getAmount

public BigDecimal getAmount()
Return the amount passed to the constructor.


getTimesDivDecimals

public int getTimesDivDecimals()
Return the number of decimals to be retained by times and div operations.

See changeTimesDivDecimals(int) for a way of altering this value from the default.


getNumDecimals

public int getNumDecimals()
Return the number of decimals in this value.

For validating the number of decimals in user input, you are highly encouraged to use Check.numDecimalsAlways(int) or Check.numDecimalsMax(int).


isPlus

public boolean isPlus()
Return true only if the amount is positive.


isMinus

public boolean isMinus()
Return true only if the amount is negative.


isZero

public boolean isZero()
Return true only if the amount is zero.


eq

public boolean eq(Decimal aThat)
Equals (insensitive to number of decimals).

That is, 10 and 10.00 are considered equal by this method.

Return true only if the amounts are equal. This method is not synonymous with the equals method, since the equals(Object) method is sensitive to the exact number of decimal places.


gt

public boolean gt(Decimal aThat)
Greater than.

Return true only if 'this' amount is greater than 'that' amount.


gteq

public boolean gteq(Decimal aThat)
Greater than or equal to.

Return true only if 'this' amount is greater than or equal to 'that' amount.


lt

public boolean lt(Decimal aThat)
Less than.

Return true only if 'this' amount is less than 'that' amount.


lteq

public boolean lteq(Decimal aThat)
Less than or equal to.

Return true only if 'this' amount is less than or equal to 'that' amount.


plus

public Decimal plus(Decimal aThat)
Add aThat Decimal to this Decimal.


minus

public Decimal minus(Decimal aThat)
Subtract aThat Decimal from this Decimal.


sum

public static Decimal sum(Collection<Decimal> aDecimals)
Sum a collection of Decimal objects. You are encouraged to use database summary functions whenever possible, instead of this method.

Parameters:
aDecimals - collection of Decimal objects. If the collection is empty, then a zero value is returned.

times

public Decimal times(long aFactor)
Multiply this Decimal by an integral factor.

The number of decimals in the return value is the same as the number of decimals of 'this' Decimal. For example,

10 x 2 = 20
  10.00 x 2 = 20.00
This conforms to most user's expectations. This behavior is slightly different from the other times methods.


times

public Decimal times(Decimal aFactor)
Multiply this Decimal by an non-integral factor (having a decimal point).

The number of decimals of the result is taken from getTimesDivDecimals().


times

public Decimal times(double aFactor)
Multiply this Decimal by an non-integral factor (having a decimal point).

The number of decimals of the result is taken from getTimesDivDecimals(). Consider using times(Decimal) as the preferred alternative.


div

public Decimal div(long aDivisor)
Divide this Decimal by an integral divisor.

The number of decimals of the result is taken from getTimesDivDecimals().


div

public Decimal div(Decimal aDivisor)
Divide this Decimal by an non-integral divisor.

The number of decimals of the result is taken from getTimesDivDecimals().


div

public Decimal div(double aDivisor)
Divide this Decimal by an non-integral divisor.

The number of decimals of the result is taken from getTimesDivDecimalsDefault(). Consider using div(Decimal) as the preferred alternative.


abs

public Decimal abs()
Return the absolute value of the amount.


negate

public Decimal negate()
Return the amount x (-1).


round

public Decimal round()
Round to an integer.

Uses getRoundingStyle().


round

public Decimal round(int aNumberOfDecimals)
Round to 0 or more decimal places.

Uses getRoundingStyle().


toString

public String toString()
Renders this Decimal in a style suitable for debugging.

Returns the amount in the format defined by BigDecimal.toPlainString().

Overrides:
toString in class Object

equals

public boolean equals(Object aThat)
Equals (sensitive to number of decimals).

That is, 10 and 10.00 are not considered equal by this method.

This implementation imitates BigDecimal.equals(java.lang.Object), which is also sensitive to the number of decimals (or 'scale'). See eq(Decimal) as well.

Overrides:
equals in class Object

hashCode

public int hashCode()
Overrides:
hashCode in class Object

compareTo

public int compareTo(Decimal aThat)
Implements the Comparable interface.

Recommended that you use the other methods such as eq(Decimal), lt(Decimal), and so on, since they have greater clarity and concision.

Specified by:
compareTo in interface Comparable<Decimal>

doubleValue

public double doubleValue()
Required by Number.

Use of floating point data is highly discouraged. This method is provided only because it's required by Number.

Specified by:
doubleValue in class Number

floatValue

public float floatValue()
Required by Number.

Use of floating point data is highly discouraged. This method is provided only because it's required by Number.

Specified by:
floatValue in class Number

intValue

public int intValue()
Required by Number.

Specified by:
intValue in class Number

longValue

public long longValue()
Required by Number.

Specified by:
longValue in class Number

Version 4.5.0

Copyright Hirondelle Systems. Published April 17, 2010 - User Guide - All Docs.