Loại dữ liệu nào bạn nên sử dụng để kiếm tiền trong Java?
Loại dữ liệu nào bạn nên sử dụng để kiếm tiền trong Java?
Câu trả lời:
Java có Currency
lớp đại diện cho mã tiền tệ ISO 4217.
BigDecimal
là loại tốt nhất để biểu thị các giá trị thập phân tiền tệ.
Joda Money đã cung cấp một thư viện để đại diện cho tiền.
Bạn có thể sử dụng API tiền và tiền tệ (JSR 354) . Bạn có thể sử dụng API này, miễn là bạn thêm các phụ thuộc phù hợp vào dự án của mình.
Đối với Java 8, hãy thêm triển khai tham chiếu sau đây làm phụ thuộc vào pom.xml
:
<dependency>
<groupId>org.javamoney</groupId>
<artifactId>moneta</artifactId>
<version>1.0</version>
</dependency>
Sự phụ thuộc này sẽ liên tục thêm javax.money:money-api
như một phụ thuộc.
Sau đó, bạn có thể sử dụng API:
package com.example.money;
import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.is;
import java.util.Locale;
import javax.money.Monetary;
import javax.money.MonetaryAmount;
import javax.money.MonetaryRounding;
import javax.money.format.MonetaryAmountFormat;
import javax.money.format.MonetaryFormats;
import org.junit.Test;
public class MoneyTest {
@Test
public void testMoneyApi() {
MonetaryAmount eurAmount1 = Monetary.getDefaultAmountFactory().setNumber(1.1111).setCurrency("EUR").create();
MonetaryAmount eurAmount2 = Monetary.getDefaultAmountFactory().setNumber(1.1141).setCurrency("EUR").create();
MonetaryAmount eurAmount3 = eurAmount1.add(eurAmount2);
assertThat(eurAmount3.toString(), is("EUR 2.2252"));
MonetaryRounding defaultRounding = Monetary.getDefaultRounding();
MonetaryAmount eurAmount4 = eurAmount3.with(defaultRounding);
assertThat(eurAmount4.toString(), is("EUR 2.23"));
MonetaryAmountFormat germanFormat = MonetaryFormats.getAmountFormat(Locale.GERMAN);
assertThat(germanFormat.format(eurAmount4), is("EUR 2,23") );
}
}
Một loại tích phân đại diện cho giá trị nhỏ nhất có thể. Nói cách khác, chương trình của bạn nên nghĩ bằng xu chứ không phải bằng đô la / euro.
Điều này sẽ không ngăn bạn có gui dịch nó trở lại đô la / euro.
BigDecimal có thể được sử dụng, giải thích tốt về lý do tại sao không sử dụng Float hoặc Double có thể được nhìn thấy ở đây: Tại sao không sử dụng Double hoặc Float để đại diện cho tiền tệ?
JSR 354: API tiền và tiền tệ
JSR 354 cung cấp API để thể hiện, vận chuyển và thực hiện các tính toán toàn diện với Tiền và Tiền tệ. Bạn có thể tải nó từ liên kết này:
JSR 354: Tải xuống API tiền và tiền tệ
Các đặc điểm kỹ thuật bao gồm những điều sau đây:
- Một API để xử lý, ví dụ như số tiền và tiền tệ
- API để hỗ trợ triển khai thay thế cho nhau
- Các nhà máy để tạo các thể hiện của các lớp thực hiện
- Chức năng tính toán, chuyển đổi và định dạng số tiền
- API Java để làm việc với Tiền và Tiền tệ, được lên kế hoạch đưa vào Java 9.
- Tất cả các lớp và giao diện đặc tả được đặt trong gói javax.money. *.
Ví dụ mẫu về JSR 354: API tiền và tiền tệ:
Một ví dụ về việc tạo Tiền tệ và in nó lên bàn điều khiển trông như thế này ::
MonetaryAmountFactory<?> amountFactory = Monetary.getDefaultAmountFactory();
MonetaryAmount monetaryAmount = amountFactory.setCurrency(Monetary.getCurrency("EUR")).setNumber(12345.67).create();
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault());
System.out.println(format.format(monetaryAmount));
Khi sử dụng API triển khai tham chiếu, mã cần thiết đơn giản hơn nhiều:
MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault());
System.out.println(format.format(monetaryAmount));
API cũng hỗ trợ tính toán với Mon MoneyAmounts:
MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmount otherMonetaryAmount = monetaryAmount.divide(2).add(Money.of(5, "EUR"));
Đơn vị tiền tệ và tiền tệ
// getting CurrencyUnits by locale
CurrencyUnit yen = MonetaryCurrencies.getCurrency(Locale.JAPAN);
CurrencyUnit canadianDollar = MonetaryCurrencies.getCurrency(Locale.CANADA);
Mon MoneyAmount có nhiều phương thức khác nhau cho phép truy cập vào loại tiền được chỉ định, số lượng, độ chính xác của nó và hơn thế nữa:
MonetaryAmount monetaryAmount = Money.of(123.45, euro);
CurrencyUnit currency = monetaryAmount.getCurrency();
NumberValue numberValue = monetaryAmount.getNumber();
int intValue = numberValue.intValue(); // 123
double doubleValue = numberValue.doubleValue(); // 123.45
long fractionDenominator = numberValue.getAmountFractionDenominator(); // 100
long fractionNumerator = numberValue.getAmountFractionNumerator(); // 45
int precision = numberValue.getPrecision(); // 5
// NumberValue extends java.lang.Number.
// So we assign numberValue to a variable of type Number
Number number = numberValue;
Tiền tệ có thể được làm tròn bằng cách sử dụng toán tử làm tròn:
CurrencyUnit usd = MonetaryCurrencies.getCurrency("USD");
MonetaryAmount dollars = Money.of(12.34567, usd);
MonetaryOperator roundingOperator = MonetaryRoundings.getRounding(usd);
MonetaryAmount roundedDollars = dollars.with(roundingOperator); // USD 12.35
Khi làm việc với các bộ sưu tập của Mon MoneyAmounts, một số phương pháp tiện ích tuyệt vời để lọc, sắp xếp và nhóm có sẵn.
List<MonetaryAmount> amounts = new ArrayList<>();
amounts.add(Money.of(2, "EUR"));
amounts.add(Money.of(42, "USD"));
amounts.add(Money.of(7, "USD"));
amounts.add(Money.of(13.37, "JPY"));
amounts.add(Money.of(18, "USD"));
Các hoạt động kiếm tiền tùy chỉnh
// A monetary operator that returns 10% of the input MonetaryAmount
// Implemented using Java 8 Lambdas
MonetaryOperator tenPercentOperator = (MonetaryAmount amount) -> {
BigDecimal baseAmount = amount.getNumber().numberValue(BigDecimal.class);
BigDecimal tenPercent = baseAmount.multiply(new BigDecimal("0.1"));
return Money.of(tenPercent, amount.getCurrency());
};
MonetaryAmount dollars = Money.of(12.34567, "USD");
// apply tenPercentOperator to MonetaryAmount
MonetaryAmount tenPercentDollars = dollars.with(tenPercentOperator); // USD 1.234567
Tài nguyên:
Xử lý tiền và tiền tệ trong Java với JSR 354
Nhìn vào API tiền và tiền tệ Java 9 (JSR 354)
Xem thêm: JSR 354 - Tiền tệ và tiền
Bạn nên sử dụng BigDecimal để thể hiện các giá trị tiền tệ. Nó cho phép bạn sử dụng nhiều chế độ làm tròn khác nhau và trong các ứng dụng tài chính, chế độ làm tròn thường là một yêu cầu khó khăn thậm chí có thể được pháp luật ủy quyền.
Tôi đã thực hiện một microbenchmark (JMH) để so sánh Moneta (java triển khai tiền tệ JSR 354) với BigDecimal về hiệu suất.
Đáng ngạc nhiên, hiệu suất BigDecimal dường như tốt hơn so với moneta. Tôi đã sử dụng cấu hình moneta sau:
org.javamoney.moneta.Money.defaults.precision = 19 org.javamoney.moneta.Money.defaults.roundingMode = HALF_UP
package com.despegar.bookedia.money;
import org.javamoney.moneta.FastMoney;
import org.javamoney.moneta.Money;
import org.openjdk.jmh.annotations.*;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.concurrent.TimeUnit;
@Measurement(batchSize = 5000, iterations = 10, time = 2, timeUnit = TimeUnit.SECONDS)
@Warmup(iterations = 2)
@Threads(value = 1)
@Fork(value = 1)
@State(Scope.Benchmark)
@BenchmarkMode(Mode.Throughput)
public class BigDecimalBenchmark {
private static final Money MONEY_BASE = Money.of(1234567.3444, "EUR");
private static final Money MONEY_SUBSTRACT = Money.of(232323, "EUR");
private static final FastMoney FAST_MONEY_SUBSTRACT = FastMoney.of(232323, "EUR");
private static final FastMoney FAST_MONEY_BASE = FastMoney.of(1234567.3444, "EUR");
MathContext mc = new MathContext(10, RoundingMode.HALF_UP);
@Benchmark
public void bigdecimal_string() {
new BigDecimal("1234567.3444").subtract(new BigDecimal("232323")).multiply(new BigDecimal("3.4"), mc).divide(new BigDecimal("5.456"), mc);
}
@Benchmark
public void bigdecimal_valueOf() {
BigDecimal.valueOf(12345673444L, 4).subtract(BigDecimal.valueOf(232323L)).multiply(BigDecimal.valueOf(34, 1), mc).divide(BigDecimal.valueOf(5456, 3), mc);
}
@Benchmark
public void fastmoney() {
FastMoney.of(1234567.3444, "EUR").subtract(FastMoney.of(232323, "EUR")).multiply(3.4).divide(5.456);
}
@Benchmark
public void money() {
Money.of(1234567.3444, "EUR").subtract(Money.of(232323, "EUR")).multiply(3.4).divide(5.456);
}
@Benchmark
public void money_static(){
MONEY_BASE.subtract(MONEY_SUBSTRACT).multiply(3.4).divide(5.456);
}
@Benchmark
public void fastmoney_static() {
FAST_MONEY_BASE.subtract(FAST_MONEY_SUBSTRACT).multiply(3.4).divide(5.456);
}
}
Kết quả là
Benchmark Mode Cnt Score Error Units
BigDecimalBenchmark.bigdecimal_string thrpt 10 479.465 ± 26.821 ops/s
BigDecimalBenchmark.bigdecimal_valueOf thrpt 10 1066.754 ± 40.997 ops/s
BigDecimalBenchmark.fastmoney thrpt 10 83.917 ± 4.612 ops/s
BigDecimalBenchmark.fastmoney_static thrpt 10 504.676 ± 21.642 ops/s
BigDecimalBenchmark.money thrpt 10 59.897 ± 3.061 ops/s
BigDecimalBenchmark.money_static thrpt 10 184.767 ± 7.017 ops/s
Xin vui lòng sửa tôi nếu tôi thiếu một cái gì đó
Đối với trường hợp đơn giản (một loại tiền tệ), nó là đủ Integer
/ Long
. Giữ tiền bằng xu (...) hoặc hàng trăm / nghìn xu (bất kỳ độ chính xác nào bạn cần với bộ chia cố định)
BigDecimal là loại dữ liệu tốt nhất để sử dụng cho tiền tệ.
Có rất nhiều container cho tiền tệ, nhưng tất cả chúng đều sử dụng BigDecimal làm kiểu dữ liệu cơ bản. Bạn sẽ không gặp trục trặc với BigDecimal, có thể sử dụng BigDecimal.ROUND_HALF_EVEN làm tròn.
Tôi thích sử dụng Tiny Type sẽ bao gồm cả hai, BigDecimal hoặc int như các câu trả lời trước đây đã đề xuất. (Tôi sẽ sử dụng gấp đôi trừ khi các vấn đề chính xác tăng lên).
Loại Tiny cung cấp cho bạn loại an toàn để bạn không nhầm lẫn một khoản tiền gấp đôi với các nhân đôi khác.