任何人都可以推荐一个类库或设计模式是车型数量的钱的?如何金钱的数量型Java中
我想它应该支持:(尊重某些四舍五入惯例(CFR即Banker's Rounding))
- 多种货币
- 一种机制来指出小数
- 数学的数
- 序列化并从字符串表示
- ...?
I.e. 19.99美元可序列化到“USD-2-00000001999”
(虽然2显示的小数位数)
任何人都可以推荐一个类库或设计模式是车型数量的钱的?如何金钱的数量型Java中
我想它应该支持:(尊重某些四舍五入惯例(CFR即Banker's Rounding))
I.e. 19.99美元可序列化到“USD-2-00000001999”
(虽然2显示的小数位数)
我就从检查monetary模块JScience项目(Jean Marie Dautelle)。
根据您的具体需求,斯蒂芬Colebourne对开始Joda-Money(“一个更集中的项目” [比JScience])前一段时间。但目前还没有完整版本(0.5版本在一年前发布)。
,这看起来像它可能会帮助,但我与它没有任何经验:http://quantlib.org/index.shtml
似乎有一个Java接口:** ** JQuantlib(http://www.jquantlib.org)。在查看API时,我不会立即看到一个“金额”类...在像Coupon这样的类中,金额被建模为双倍。我可能会误解。无论如何,Thx! – Jan 2010-11-10 15:10:07
乔达货币看起来很有希望,以及:http://www.joda.org/joda-money/ (不知道它是怎么生产做好准备,虽然)
除了Paul所说的,还有Martin Fowler的Money Pattern。
有JSR 354 JavaMoney应该成为Java 9的一部分。 查看presentation了解更多详情。
此JSR应该是Joda Money的替代品,但目前只有Joda Money稳定并在生产中进行测试。
您也可以承担这个图书馆一看:
JScience - 用货币单位
Time and Money( “需要阿尔法用户”)
这是Martin Fowler分析模板的java money类的完整示例RN:
package com.console.utils.value;
import com.console.core.exceptions.UnknownCurrencyCodeException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.Currency;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.Assert;
import static java.math.RoundingMode.HALF_UP;
/**
*
* @author farouka
*/
public class Money implements Serializable {
/**
* Why me
*/
private static final int[] cents = new int[]{1, 10, 100, 1000};
private BigDecimal amount;
private Currency currency;
//private MathContext DEFAULT_CONTEXT = new MathContext(2, HALF_UP);
private MathContext DEFAULT_CONTEXT = new MathContext(10, RoundingMode.HALF_DOWN);
public Money(long amount, Currency currency) {
this.currency = currency;
this.amount = BigDecimal.valueOf(amount, currency.getDefaultFractionDigits());
}
/**
* Creates a currency object from the long value provided assuming the long value
* represents the base currency in the least monetary unit. For eg, new Money(500, "GBP")
* is assumed to mean 5.00 great british pounds
* @param amount in base monetary unit
* @param currCode
* @throws com.console.core.exceptions.UnknownCurrencyCodeException
*/
public Money(long amount, String currCode) throws UnknownCurrencyCodeException {
this(amount, Currency.getInstance(currCode));
}
/**
* Construct an IMMUTABLE money object from a double. It is assumed that
* the whole part of the double is the Money with the fractional part representing
* lowest denominator of the currency. For eg, new Money (50.99, "GBP") is assumed
* to be 50 pounds and 99 pence.
* PS. 89.788 will be truncated to 89.78 based on the defaultcurrencydigit of the currency
* @param amount
* @param curr
*/
public Money(double amount, Currency curr) {
this.currency = curr;
BigDecimal bd = BigDecimal.valueOf(amount);
this.amount = bd.setScale(centFactor(), HALF_UP);
}
private Money() {
}
/**
* Construct an IMMUTABLE money object from a double. It is assumed that
* the whole part of the double is the Money with the fractional part representing
* lowest denominator of the currency. For eg, new Money (50.99, "GBP") is assumed
* to be 50 pounds and 99 pence.
* PS. 89.788 will be truncated to 89.78 based on the defaultcurrencydigit of the currency
* code supplied
* @param amount
* @param currCode iso 4217 currency code
* @throws com.console.core.exceptions.UnknownCurrencyCodeException
*/
public Money(double amount, String currCode) throws UnknownCurrencyCodeException {
this.currency = Currency.getInstance(currCode);
BigDecimal bd = BigDecimal.valueOf(amount);
this.amount = bd.setScale(currency.getDefaultFractionDigits(), HALF_UP);
}
/**
* Constructs an IMMUTABLE money from a BigDecimal. the BigDecimal provided is only scaled
* to used the default digits in currency object represented by the sting parameter
* @param bigDecimal
* @param currCode ISO 4217 cuurency code
* @throws com.console.core.exceptions.UnknownCurrencyCodeException
*/
public Money(BigDecimal bigDecimal, String currCode) throws UnknownCurrencyCodeException {
this.currency = Currency.getInstance(currCode);
this.amount = bigDecimal.setScale(currency.getDefaultFractionDigits(), HALF_UP);
}
/**
* Constructs an IMMUTABLE money from a BigDecimal. the BigDecimal provided is only scaled
* to used the default digits in currency object represented by the sting parameter
* @param multiply
* @param currency
*/
public Money(BigDecimal bigDecimal, Currency currency) {
this.currency = currency;
this.amount = bigDecimal.setScale(currency.getDefaultFractionDigits(), HALF_UP);
}
// public boolean assertSameCurrencyAs(Money arg) {
// return this.currency.getCurrencyCode().equals(arg.currency.getCurrencyCode());
// }
//
public boolean assertSameCurrencyAs(Money money) throws IncompatibleCurrencyException{
if (this.currency == null) {
throw new IncompatibleCurrencyException("currency.invalid");
}
if (money == null) {
throw new IncompatibleCurrencyException("currency.invalid");
}
Assert.assertEquals("money math mismatch", currency, money.currency);
return true;
}
private int centFactor() {
return cents[ getCurrency().getDefaultFractionDigits() ];
}
public BigDecimal amount() {
return amount;
}
public long amountAsLong(){
return amount.unscaledValue().longValue();
}
public Currency getCurrency() {
return currency;
}
// common currencies
public static Money dollars(double amount) {
Money result = null;
try {
result = new Money(amount, "USD");
} catch (UnknownCurrencyCodeException ex) {
Logger.getLogger(Money.class.getName()).log(Level.SEVERE, null, ex);
}
return result;
}
public static Money dollars(long amount) {
Money result = null;
try {
result = new Money(amount, "USD");
} catch (UnknownCurrencyCodeException ex) {
Logger.getLogger(Money.class.getName()).log(Level.SEVERE, null, ex);
}
return result;
}
public static Money pounds(double amount) {
Money result = null;
try {
result = new Money(amount, "GBP");
} catch (UnknownCurrencyCodeException ex) {
Logger.getLogger(Money.class.getName()).log(Level.SEVERE, null, ex);
}
return result;
}
public static Money pounds(long amount) {
Money result = null;
try {
result = new Money(amount, "GBP");
} catch (UnknownCurrencyCodeException ex) {
Logger.getLogger(Money.class.getName()).log(Level.SEVERE, null, ex);
}
return result;
}
public static Money pounds(BigDecimal amount) {
Money result = null;
try {
result = new Money(amount, "GBP");
} catch (UnknownCurrencyCodeException ex) {
Logger.getLogger(Money.class.getName()).log(Level.SEVERE, null, ex);
}
return result;
}
@Override
public int hashCode() {
int hash = (int) (amount.hashCode()^(amount.hashCode() >>> 32));
return hash;
}
@Override
public boolean equals(Object other) {
return (other instanceof Money && equals((Money) other));
}
public boolean equals(Money other) {
return (currency.equals(other.currency) && (amount.equals(other.amount)));
}
public Money add(Money other) throws Exception{
assertSameCurrencyAs(other);
return newMoney(amount.add(other.amount, DEFAULT_CONTEXT));
}
private int compareTo(Money money) throws Exception {
assertSameCurrencyAs(money);
return amount.compareTo(money.amount);
}
public Money multiply(BigDecimal amount) {
return new Money(this.amount().multiply(amount, DEFAULT_CONTEXT), currency);
}
public Money multiply(BigDecimal amount, RoundingMode roundingMode) {
MathContext ct = new MathContext(currency.getDefaultFractionDigits(), roundingMode);
return new Money(amount().multiply(amount, ct), currency);
}
private Money newMoney(BigDecimal amount) {
return new Money(amount, this.currency);
}
public Money multiply(double amount) {
return multiply(new BigDecimal(amount));
}
public Money subtract(Money other) throws Exception {
assertSameCurrencyAs(other);
return newMoney(amount.subtract(other.amount, DEFAULT_CONTEXT));
}
public int compareTo(Object other) throws Exception {
return compareTo((Money) other);
}
public boolean greaterThan(Money other)throws Exception {
return (compareTo(other) > 0);
}
// public Money[] allocate(int n){
// Money lowResult = newMoney(amount.unscaledValue().longValue()/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 = 0; i < n; i++) results[i] = lowResult;
//
// return results;
// }
//
// 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;
//
// }
public Money divideByNumber(double divisor){
BigDecimal div = BigDecimal.valueOf(divisor);
BigDecimal ans = this.amount.divide(div, DEFAULT_CONTEXT);
return new Money(ans, this.currency);
}
public int getQuotient(Money divisor){
BigDecimal ans = this.amount.divide(divisor.amount, RoundingMode.DOWN);
return ans.intValue();
}
/**
* divides toe moneys and return the quotient and Remainder this method has been customised,
* for my money transfer needs...sorry
* @param divisor
* @return
*/
public int[] getQuotientandRemainder(Money divisor){
int[] ans = new int[2];
BigDecimal[] bdArr = this.amount.divideAndRemainder(divisor.amount, DEFAULT_CONTEXT);
BigDecimal quo = bdArr[0];
BigDecimal rem = bdArr[1];
ans[0] = quo.intValue();
if(rem.compareTo(BigDecimal.ZERO) == 0){
ans[1] =0;
}else{
ans[1] = 1;
}
return ans;
}
public String toFormattedString() {
NumberFormat nf = NumberFormat.getCurrencyInstance();
nf.setCurrency(currency);
nf.setGroupingUsed(true);
nf.setMaximumFractionDigits(currency.getDefaultFractionDigits());
return nf.format(this.amount.doubleValue());
}
/**
* Returns the ISO-4217 currency code of the currency
* attached to this money.
*
* @return The ISO-4217 currency code.
*/
public String getCurrencyCode() {
return currency.getCurrencyCode();
}
@Override
public String toString() {
return amount.toString();
}
/**
* Returns the precision for this money. The precision is the total number
* of digits that the value can represent. This includes the integer part.
* So, 18 would be able to represent:
*
* 1234567890.12345678
*
* 123456789.78
*
* 123456789
*
* 0.123456789
*
* @return The precision.
*/
public int precision() {
return amount.precision();
}
/**
* Returns the 'scale' for this money. The scale is the number of
* digits that are moved to the fractional part, assuming that all
* digits are represented by a single integer value. For example:
*
* If: 123456789has scaling 2, it would be :
*
* 123456789.78
*
* @return The scale value.
*/
public int scale() {
return amount.scale();
}
/**
* Returns the sign for the money (negative or positive).
* -1 if negative, 0 if 0.00 (zero), 1 if positive.
*
* @return The sign of the money.
*/
public int signum() {
return amount.signum();
}
}
And here is the UnknownCurrencyCodeException class
package com.console.lib.utils;
/**
* An exception which is raised when an unrecognised currency code is passed to the
* Currency class.
*
* @author Farouk Alhassan
* @see Currency
*/
public class UnknownCurrencyCodeException extends Exception {
// Reason for exception
private String reason = null;
/**
* Create a new unknown currency code exception.
*
* @param reason for the exception
*/
public UnknownCurrencyCodeException(String reason) {
this.reason = reason;
}
/**
* Return the reason this exception was raised.
*
* @return the reason why the string isn't a valid currency code
*/
public String getReason() {
return reason;
}
/**
* Convert the exception to a string
*
* @return string version of the exception
*/
public String toString() {
return getReason();
}
}
感谢farouka在http://cameotutorials.blogspot.com/2009/06/money-class-for-use-in-currency.html
Joda-Money看起来不错!但自2009年以来发展似乎停止了! ......有时间跳板并帮助开发人员。 – Elister 2011-07-06 06:28:44
Joda-Money非常稳定,所以不需要太多的开发。这就是说,我会很感激反馈,评论和小改进建议(例如GitHub forks) – JodaStephen 2012-01-18 11:34:43
版本0.10.0现在在那里。对我来说,它是经过充分测试和相当稳定。这个库解决了我们所有的金钱计算。 – mahesh 2015-06-08 01:25:46