Jenis data apa yang digunakan untuk uang di Jawa? [Tutup]


183

Jenis data apa yang harus Anda gunakan untuk uang di Jawa?


2
Tergantung operasi apa yang akan Anda lakukan. Tolong tawarkan informasi lebih lanjut.
eversor

@ eversor Bisakah Anda memberi saya deskripsi tipe data apa yang harus digunakan untuk operasi yang berbeda?
questborn

1
Sedang melakukan perhitungan yang mengharuskan saya untuk secara akurat mewakili sen.
questborn

Apakah Anda dapat meramalkan jumlah uang terbesar yang perlu ditangani oleh aplikasi Anda? Dan, kalkulasi Anda, apakah akan sederhana (tambahan dll) atau operasi keuangan yang lebih kompleks?
eversor

Jawaban:


133

Java memiliki Currencykelas yang mewakili kode mata uang ISO 4217. BigDecimaladalah jenis terbaik untuk mewakili nilai desimal mata uang.

Joda Money telah menyediakan perpustakaan untuk mewakili uang.


5
Mengapa kita tidak bisa menggunakan float atau double?
Erran Morad

20
@ Borat Sagdiyev Inilah alasannya . Anda juga dapat merujuk ini .
Buhake Sindi

2
@ Borat: Anda bisa jika tahu apa yang Anda lakukan, lihat artikel ini oleh Peter Lawrey. tetapi tampaknya setidaknya sama besar kerumitan untuk melakukan semua pembulatan untuk menggunakan BigDecimal.
Nathan Hughes

35
"Jika saya punya uang receh untuk setiap kali saya melihat seseorang menggunakan FLOAT untuk menyimpan mata uang, saya akan memiliki $ 999.997634" - Bill Karwin
Collin Krawll

36

Anda dapat menggunakan API Uang dan Mata Uang (JSR 354) . Anda dapat menggunakan API ini di, asalkan Anda menambahkan dependensi yang sesuai untuk proyek Anda.

Untuk Java 8, tambahkan implementasi referensi berikut sebagai ketergantungan pada Anda pom.xml:

<dependency>
    <groupId>org.javamoney</groupId>
    <artifactId>moneta</artifactId>
    <version>1.0</version>
</dependency>

Ketergantungan ini akan ditambahkan javax.money:money-apisecara positif sebagai ketergantungan.

Anda kemudian dapat menggunakan 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") );
    }
}

Bagaimana dengan serialisasi dan penyimpanan ke db? Format apa yang harus digunakan untuk mengirim melalui kawat?
Paweł Szczur

1
Saya percaya bahwa Oracle mendedikasikan againts termasuk Java Money di Java 9. Sungguh memalukan. Tapi jawaban yang bagus. Kita masih bisa menggunakannya dengan Maven
borjab

3
Apakah Anda memiliki sumber untuk Oracle yang memutuskan untuk memasukkan Java Money dalam Java 9?
Abdull

25

Tipe integral yang mewakili nilai sekecil mungkin. Dengan kata lain program Anda harus berpikir dalam sen bukan dalam dolar / euro.

Ini seharusnya tidak menghentikan Anda dari meminta gui menerjemahkannya kembali ke dolar / euro.


Ingatlah bahwa jumlah uang dapat meluap ukuran int
eversor

5
@Everor yang akan membutuhkan lebih dari 20 juta dolar sebagian besar aplikasi tidak akan membutuhkan sebanyak itu jika mereka melakukan cukup lama karena bahkan pemerintah kita tidak dapat menangani cukup uang untuk melimpah itu
ratchet freak

4
@ scratchetfreak Mungkin lebih baik menggunakan waktu yang lama.
trognanders

5
Banyak bank menangani jumlah uang yang jauh lebih besar yaitu $ 20.000.000 setiap hari. Ini bahkan tidak memperhitungkan mata uang seperti yen dengan nilai tukar besar terhadap dolar. Jenis integer mungkin yang terbaik untuk menghindari masalah pembulatan meskipun mereka menjadi berantakan dengan perhitungan bunga dan nilai tukar. Namun, tergantung pada aplikasinya, Anda mungkin memerlukan tipe integer 64-bit.
Alchymist

Idealnya microdollars, sebenarnya, saat itu jika Anda lakukan misalnya $ 10/3 maka kesalahan pembulatan (3333.3 => 3333.0) tidak terlalu mempengaruhi nilai akhir (dalam hal ini tidak mempengaruhi nilai sebenarnya sama sekali, meskipun itu berbahaya untuk menganggap bahwa itu tidak akan pernah terjadi). Ini sangat penting jika Anda melakukan banyak perhitungan berturut-turut sebelum pengguna melihat hasilnya, karena kesalahan pembulatan akan bertambah.
Chris Browne


11

JSR 354: API Uang dan Mata Uang

JSR 354 menyediakan API untuk mewakili, mengangkut, dan melakukan perhitungan komprehensif dengan Uang dan Mata Uang. Anda dapat mengunduhnya dari tautan ini:

JSR 354: Unduhan API Uang dan Mata Uang

Spesifikasi terdiri dari hal-hal berikut:

  1. API untuk penanganan misalnya jumlah uang dan mata uang
  2. API untuk mendukung implementasi yang dapat dipertukarkan
  3. Pabrik untuk membuat instance dari kelas implementasi
  4. Fungsi untuk perhitungan, konversi, dan pemformatan jumlah uang
  5. API Java untuk bekerja dengan Uang dan Mata Uang, yang rencananya akan dimasukkan dalam Java 9.
  6. Semua kelas spesifikasi dan antarmuka terletak di paket javax.money. *.

Contoh Contoh JSR 354: API Uang dan Mata Uang:

Contoh membuat MonetaryAmount dan mencetaknya ke konsol terlihat seperti ini ::

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));

Saat menggunakan API implementasi referensi, kode yang diperlukan jauh lebih sederhana:

MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault());
System.out.println(format.format(monetaryAmount));

API juga mendukung perhitungan dengan MonetaryAmounts:

MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmount otherMonetaryAmount = monetaryAmount.divide(2).add(Money.of(5, "EUR"));

CurrencyUnit dan MonetaryAmount

// getting CurrencyUnits by locale
CurrencyUnit yen = MonetaryCurrencies.getCurrency(Locale.JAPAN);
CurrencyUnit canadianDollar = MonetaryCurrencies.getCurrency(Locale.CANADA);

MonetaryAmount memiliki berbagai metode yang memungkinkan mengakses mata uang yang ditetapkan, jumlah numerik, ketepatannya, dan lainnya:

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;

Jumlah Moneter dapat dibulatkan menggunakan operator pembulatan:

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

Saat bekerja dengan koleksi MonetaryAmounts, beberapa metode utilitas yang bagus untuk memfilter, menyortir, dan mengelompokkan tersedia.

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"));

Operasi MonetaryAmount khusus

// 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

Sumber:

Menangani uang dan mata uang di Jawa dengan JSR 354

Melihat ke dalam Java 9 Money and Currency API (JSR 354)

Lihat Juga: JSR 354 - Mata Uang dan Uang


Semua ini bagus, tetapi seperti yang disarankan Federico di atas, kelihatannya lebih lambat daripada BigDecimal :-)) hanya lelucon buruk, tetapi saya akan memberikan tes itu sekarang 1 tahun kemudian ...
kensai

6

Anda harus menggunakan BigDecimal untuk mewakili nilai moneter. Ini memungkinkan Anda untuk menggunakan berbagai mode pembulatan , dan dalam aplikasi keuangan, mode pembulatan seringkali merupakan persyaratan keras yang bahkan mungkin diamanatkan oleh hukum.



6

Saya telah melakukan microbenchmark (JMH) untuk membandingkan Moneta (implementasi JSR 354 mata uang java) dengan BigDecimal dalam hal kinerja.

Anehnya, kinerja BigDecimal tampaknya lebih baik daripada moneta. Saya telah menggunakan konfigurasi moneta berikut:

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);
    }
}

Yang menghasilkan

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

Silakan koreksi saya jika saya kehilangan sesuatu


Menarik, saya akan menjalankan tes yang sama dengan hal-hal terbaru pada JDK9
kensai

4

Untuk kasus sederhana (satu mata uang) sudah cukup Integer/ Long. Simpan uang dalam sen (...) atau seperseratus / seperseribu sen (presisi apa pun yang Anda butuhkan dengan pembagi tetap)


3

BigDecimal adalah tipe data terbaik yang digunakan untuk mata uang.

Ada banyak wadah untuk mata uang, tetapi mereka semua menggunakan BigDecimal sebagai tipe data yang mendasarinya. Anda tidak akan salah dengan BigDecimal, mungkin menggunakan BigDecimal.ROUND_HALF_EVEN pembulatan.


2

Saya suka menggunakan Tiny Type yang akan membungkus ganda, BigDecimal, atau int seperti jawaban sebelumnya telah menyarankan. (Saya akan menggunakan ganda kecuali masalah presisi muncul).

Tipe Tiny memberi Anda keamanan tipe sehingga Anda tidak bingung dengan uang ganda dengan ganda lainnya.


6
Sementara saya juga seperti jenis kecil, Anda harus Jangan pernah menggunakan ganda untuk menyimpan nilai moneter.
orien
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.