我知道 BigDecimal 是在 Java 中表示货币价值的推荐最佳实践。你用什么?有没有你更喜欢使用的更好的库?
14 回答
BigDecimal
一路走来。我听说有些人创建了他们自己的Cash
或Money
用货币封装现金价值的类,但在皮肤下它仍然是一个BigDecimal
,可能是BigDecimal.ROUND_HALF_EVEN
四舍五入。
编辑:正如 Don 在他的回答中提到的那样,有一些像timeandmoney这样的开源项目,虽然我赞扬他们试图阻止开发人员重新发明轮子,但我对使用 pre-alpha 库没有足够的信心它在生产环境中。此外,如果您在引擎盖下四处挖掘,您会发现他们也BigDecimal
使用.
通过搜索引擎到达这里以了解 JodaMoney 的人们可能会很有用:http: //www.joda.org/joda-money/。
我不是在这里表达我的观点,但是有很多反对 BigDecimal 的很好的论据,有人可能应该扔掉:
http://lemnik.wordpress.com/2011/03/25/bigdecimal-and-your-money/
我之前遇到的一个方便的库是Joda-Money库。它的实现之一确实基于 BigDecimal。它基于ISO-4217货币规范,可以支持自定义货币列表(通过 CVS 加载)。
这个库有少量文件,如果需要修改,可以快速浏览。Joda-Money 在 Apache 2.0 许可下发布。
如果您只使用美元和美分,我会使用 long(偏移 2 个小数位)。如果您需要更多细节,大十进制可能是要走的路。
无论哪种方式,我可能会扩展该类以拥有一个使用正确格式的 .toString() ,并作为放置可能出现的其他方法的地方(如果小数是很长的,乘法和除法会出错没有调整)
此外,如果您使用定义自己的类和接口,那么您可以随意替换实现。
BigDecimal
或另一种定点表示通常是金钱所需要的。
浮点 ( Double
, Float
) 表示和计算不精确,导致错误结果。
在处理时间和金钱时,你必须非常小心。
当您使用金钱时,我希望每个人都应该知道永远不要使用浮点数或双精度数。
但我不确定 BigDecimal。
在大多数情况下,如果您只跟踪 int 或 long 中的美分,就可以了。这样你永远不会处理小数位。
您仅在打印时显示美元。始终使用整数使用 cents internal。如果需要除法或需要使用 Math.abs(),这可能会很棘手。
但是,您可能会关心半分钱,甚至百分之一分钱。我不知道有什么好方法可以做到这一点。您可能只需要处理千分之一美分并使用多头。或者你可能会被迫使用 BigDecimal
我会对此做更多的阅读,但忽略所有开始谈论使用浮点数或双精度数来代表金钱的人。他们只是自找麻烦。
我觉得我的建议不完整,所以请多加补充。您正在处理危险类型!
创建一个 Money 类是要走的路。在下面使用 BigDecimal(甚至是 int)。然后使用 Currency 类来定义舍入约定。
不幸的是,没有运算符重载,Java 使得创建这样的基本类型变得非常不愉快。
有一个更好的图书馆,timeandmoney。IMO,它远远优于 JDK 提供的用于表示这两个概念的库。
绝对不是 BigDecimal。舍入和表示有很多特殊规则,您必须担心。
Martin Fowler 建议实现一个专门的Money类来表示货币金额,并且它还实现了货币转换的规则。
嘿,这是一篇关于 BigDecimal 的非常有趣的文章,以及为什么有时使用它而不是双精度数的说明性示例。BigDecimal 教程。
最终显示货币值时,您可以使用 DecimalFormat 类。它提供本地化支持并且非常可扩展。
我会将 BigDecimal 封装在 Money 类中,就像上面提到的那样,它也有货币。重要的是你要进行大量的单元测试,尤其是在使用不同的货币时。此外,如果您添加一个方便的构造函数,该构造函数接受一个字符串或一个执行相同操作的工厂方法,这样您就可以编写类似这样的测试,这也是一个好主意:
assertEquals(Money.create("100.0 USD").add("10 GBP"),Money.create("116 USD"));
总是有限制和细节。任何没有足够经验来理解以下文章中概述的微妙问题的人都应该在处理真实世界的财务数据之前认真重新考虑:
http://lemnik.wordpress.com/2011/03/25/bigdecimal-and-your-money
BigDecimal 并不是唯一正确的表示或唯一的拼图。在特定条件下,使用由存储为整数的美分支持的 Money 类可能就足够了,并且比 BigDecimal 快得多。是的,这意味着使用美元作为货币并限制金额,但这种限制对于许多用例来说是完全可以接受的,而且所有货币都有四舍五入和小面额的特殊情况,因此没有“通用”解决方案。