Money class
Many applications operate with money, but in most programming languages, including Java, there is no standard class Money. Many simply store monetary values in a variable with a floating point, but this is a bad decision, since there are always problems with rounding, which have to be solved differently each time. In addition, money can be measured in different sizes (rubles, dollars, euros and others). You cannot add or compare the euro with the ruble - this will inevitably cause an error as a result.
When implementing the money class, the first thing to do is choose the data type for storing the amount of money. The first thing that comes to mind is to use a floating point type. This will not be a good solution. The fact is that floating point calculations do not always lead to the desired result. Try the following example:
Therefore, it is better to choose an integer type and express it in the smallest units for the selected currency: in kopecks or cents. For currency, it is better to use the standard Java data type Currency.
The class that I got:
And the JUnit test for it:
The article is based on Martin Fowler’s book “Enterprise Application Architecture”.
When implementing the money class, the first thing to do is choose the data type for storing the amount of money. The first thing that comes to mind is to use a floating point type. This will not be a good solution. The fact is that floating point calculations do not always lead to the desired result. Try the following example:
double val = 0.00;
for (int i = 0; i <10; i ++)
val + = 0.10;
System.out.println (val == 1.00); * This source code was highlighted with Source Code Highlighter .
Therefore, it is better to choose an integer type and express it in the smallest units for the selected currency: in kopecks or cents. For currency, it is better to use the standard Java data type Currency.
The class that I got:
package money;
import java.util.Currency;
import java.util.Locale;
import org.junit.Assert;
/ **
* Created by IntelliJ IDEA.
* User: Anthony
* Date:
09/14/2008
* Time: 15:25:26 * To change this template use File | Settings | File Templates.
* /
public class Money {
private long amount;
private currency currency;
/ **
* return The numeric value of money.
* /
public double getAmount () {
return (double) amount / centFactor ();
}
/ **
* return Current currency
* /
public Currency getCurrency () {
return currency;
}
public Money (long amount, Currency currency)
{
this.currency = currency;
this.amount = amount * centFactor ();
}
public Money (double amount, Currency currency)
{
this.currency = currency;
this.amount = Math.round (amount * centFactor ());
}
private Money ()
{
}
public static Money dollars (double amount)
{
return new Money (amount, Currency.getInstance (Locale.US));
}
public static Money locale (double amount)
{
return new Money (amount, Currency.getInstance (Locale.getDefault ()));
}
public boolean equals (Object o) {
if (this == o) return true;
if (o == null || getClass ()! = o.getClass ()) return false;
Money money = (Money) o;
if (amount! = money.amount) return false;
if (currency! = null?! currency.equals (money.currency): money.currency! = null) return false;
return true;
}
public int hashCode () {
int result;
result = (int) (amount ^ (amount >>> 32));
result = 31 * result + (currency! = null? currency.hashCode (): 0);
return result;
}
/ **
* Function adds money.
* param other with which to lay down.
* return The result of addition.
* /
public Money add (Money other)
{
assertSameCurrencyAs (other);
return newMoney (amount + other.amount);
}
/ **
* Function deducts money.
* param other being scrubbed.
* return The result of the subtraction.
* /
public Money subtract (Money other)
{
assertSameCurrencyAs (other);
return newMoney (amount - other.amount);
}
/ **
* Function multiplies money by a factor.
* param arg cleaned.
* return The result of the multiplication.
* /
public Money multiply (double arg)
{
return new Money (getAmount () * arg, currency);
}
public int compareTo (Object other)
{
return compareTo ((Money) other);
}
/ **
* The function of comparing money.
* param other with which to compare
* return
* -1 less
* 0 is
* 1 more
* /
public int compareTo (Money other)
{
assertSameCurrencyAs (other);
if (amount <other.amount)
return -1;
if (amount == other.amount)
return 0;
return 1;
}
/ **
* Is there more money.
* param other with what to compare.
* return True if greater.
* /
public boolean greaterThan (Money other)
{
return (compareTo (other)> 0);
}
/ **
* Divide the money into several parts.
* param n number of parts.
* return Array of divided money.
* /
public Money [] allocate (int n)
{
Money lowResult = newMoney (amount / n);
Money highResult = newMoney (lowResult.amount + 1);
Money [] results = new Money [n];
int remainder = (int) amount% n;
for (int i = 0; i <remainder; i ++)
results [i] = highResult;
for (int i = remainder; i <n; i ++)
results [i] = lowResult;
return results;
}
/ **
* Divides money into unequal parts.
* param ratios proportion for separation.
* return Array of divided money.
* /
public Money [] allocate (long [] ratios) {
long total = 0;
for (int i = 0; i <ratios.length; i ++)
total + = ratios [i];
long remainder = amount;
Money [] results = new Money [ratios.length];
for (int i = 0; i <results.length; i ++)
{
results [i] = newMoney (amount * ratios [i] / total);
remainder - = results [i] .amount;
}
for (int i = 0; i <remainder; i ++) {
results [i] .amount ++;
}
return results;
}
private static final int [] cents = new int [] {1, 10, 100, 1000};
private int centFactor ()
{
return cents [currency.getDefaultFractionDigits ()];
}
private void assertSameCurrencyAs (Money arg)
{
Assert.assertEquals ("money math mismatch", currency, arg.currency);
}
private Money newMoney (long amount)
{
Money money = new Money ();
money.currency = this.currency;
money.amount = amount;
return money;
}
}
* This source code was highlighted with Source Code Highlighter .
And the JUnit test for it:
package test.money;
import junit.framework.Test;
import junit.framework.TestSuite;
import junit.framework.TestCase;
import junit.framework.Assert;
import money.Money;
/ **
* Money Tester.
*
* author
* since09/14/2008
* version 1.0
* /
public class MoneyTest extends TestCase {
public MoneyTest (String name) {
super (name);
}
public void setUp () throws Exception {
super.setUp ();
}
public void tearDown () throws Exception {
super.tearDown ();
}
/ **
*
* Method: add (Money other)
*
* /
public void testAdd () throws Exception {
Money m1 = Money.dollars (1.316);
Money m2 = Money.dollars (1.291);
Assert.assertEquals (m1.add (m2), Money.dollars (2.61));
}
/ **
*
* Method: subtract (Money other)
*
* /
public void testSubtract () throws Exception {
Money m1 = Money.dollars (1.316);
Money m2 = Money.dollars (1.291);
Assert.assertEquals (m1.subtract (m2), Money.dollars (0.03));
}
/ **
*
* Method: compareTo (Object other)
*
* /
public void testCompareTo () throws Exception {
Money m1 = Money.dollars (1.316);
Money m2 = Money.dollars (1.313);
Assert.assertEquals (m1.compareTo (m2), 1);
}
/ **
*
* Method: multiply (double arg)
*
* /
public void testMultiply () throws Exception {
Money m1 = Money.dollars (1.316);
Assert.assertEquals (m1.multiply (1.333), Money.dollars (1.76));
}
/ **
*
* Method: allocate (int n)
*
* /
public void testAllocateN () throws Exception {
Money m [] = Money.dollars (1.35) .allocate (3);
Assert.assertEquals (m [0] .add (m [1]) .add (m [2]), Money.dollars (1.35));
}
/ **
*
* Method: allocate (long [] ratios)
*
* /
public void testAllocateRatios () throws Exception {
long [] allocation = {3,7};
Money [] result = Money.dollars (0.05) .allocate (allocation);
assertEquals (Money.dollars (0.02), result [0]);
assertEquals (Money.dollars (0.03), result [1]);
}
public static Test suite () {
return new TestSuite (MoneyTest.class);
}
}
* This source code was highlighted with Source Code Highlighter .
The article is based on Martin Fowler’s book “Enterprise Application Architecture”.