Java'da para için hangi veri türü kullanılır? [kapalı]


183

Java'da para için hangi veri türünü kullanmalısınız?


2
Hangi işlemleri yapacağınıza bağlıdır. Lütfen daha fazla bilgi sunun.
eversor

@eversor Farklı işlemler için hangi veri türünün kullanılması gerektiğini açıklayabilir misiniz?
questborn

1
Sentleri doğru bir şekilde temsil etmemi gerektiren hesaplamalar yapıyorum.
questborn

Uygulamanızın işlemesi gereken en büyük parayı önceden tahmin edebiliyor musunuz? Ve hesaplamalarınız, basit (aditions vb.) Veya daha karmaşık finansal işlemler olacak mı?
eversor

Yanıtlar:


133

Java, CurrencyISO 4217 para birimi kodlarını temsil eden bir sınıfa sahiptir . BigDecimalpara birimi ondalık değerlerini temsil etmek için en iyi türdür.

Joda Money parayı temsil edecek bir kütüphane sağlamıştır.


5
Bunun yerine neden float veya double kullanamıyoruz?
Erran Morad

20
@Borat Sagdiyev Nedeni budur . Ayrıca, başvurabilirsiniz bu .
Buhake Sindi

2
@Borat: Ne yaptığınızı biliyorsanız, Peter Lawrey'in bu makalesine bakın . ancak en azından BigDecimals kullanmak için tüm yuvarlamayı yapmak için bir güçlük gibi görünüyor.
Nathan Hughes

35
"Birisinin para birimini saklamak için FLOAT kullandığını her gördüğümde bir kuruşum olsaydı, $ 999.997634 olurdu" - Bill Karwin
Collin

36

Sen kullanabilirsiniz Para ve Döviz API (JSR 354) . Projenize uygun bağımlılıklar eklemeniz koşuluyla bu API'yı içinde kullanabilirsiniz.

Java 8 için, aşağıdaki başvuru uygulamasını bağımlılığınıza ekleyin pom.xml:

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

Bu bağımlılık geçici javax.money:money-apiolarak bağımlılık olarak eklenecektir .

Daha sonra API'yı kullanabilirsiniz:

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

Serileştirme ve db'ye kaydetme ne olacak? Kablo üzerinden göndermek için hangi format kullanılmalıdır?
Paweł Szczur

1
Oracle'ın Java 9'da Java Money dahil olmak üzere tekrarları ayırdığını düşünüyorum. Gerçekten bir utanç. Ama harika bir cevap. Hala Maven ile kullanabiliriz
borjab

3
Oracle için Java 9'u Java 9'a dahil etmeye karar verme kaynağınız var mı?
Abdull

25

Mümkün olan en küçük değeri temsil eden integral tip. Başka bir deyişle, programınız sent / dolar cinsinden değil sent cinsinden düşünmelidir.

Bu, GUI'nin dolar / avroya geri çevirmesini engellememelidir.


Para miktarının int
eversor'un

5
@eversor, 20 milyon dolardan fazla ihtiyaç duyacak çoğu uygulama, uzun bir süre yaparlarsa, valilerimizin bile taşması için yeterli paraya sahip olmadığı için yeterli olmayacaktır
cırcır ucube

4
@ratchetfreak O zaman uzun kullanmak muhtemelen daha iyi.
trognanders

5
Birçok banka, her gün 20.000.000 $ 'lık çok daha fazla parayı ele alıyor. Bu, dolar gibi büyük döviz kurları olan yen gibi para birimlerini hesaba katmaz. Tamsayı türleri, faiz ve döviz kuru hesaplamaları ile dağınık olmalarına rağmen, yuvarlama problemlerinden kaçınmak için en iyisi olabilir. Ancak, uygulamaya bağlı olarak, 64 bitlik bir tamsayı türüne ihtiyacınız olabilir.
Simyacı

İdeal olarak mikro dolarlar, aslında, örneğin 10 $ / 3 yaparsanız, yuvarlama hatası (3333.3 => 3333.0) nihai değeri o kadar etkilemez (bu durumda gerçek değeri hiç etkilemez, ancak asla olmayacağını varsaymak tehlikeli). Bu, yuvarlama hataları bir araya geleceğinden, kullanıcınız sonucu görmeden önce arka arkaya çok sayıda hesaplama yapıyorsanız özellikle önemlidir.
Chris Browne


11

JSR 354: Para ve Para Birimi API'sı

JSR 354, Para ve Para Birimi ile kapsamlı hesaplamaları temsil etmek, taşımak ve gerçekleştirmek için bir API sağlar. Bu bağlantıdan indirebilirsiniz:

JSR 354: Para ve Para Birimi API'sı İndir

Şartname aşağıdaki şeylerden oluşur:

  1. Parasal tutarlar ve para birimlerini işlemek için bir API
  2. Değiştirilebilir uygulamaları destekleyen API'ler
  3. Uygulama sınıflarının örneklerini oluşturan fabrikalar
  4. Parasal tutarların hesaplanması, dönüştürülmesi ve biçimlendirilmesi için işlevsellik
  5. Java 9'a eklenmesi planlanan Para ve Para Birimleri ile çalışmak için Java API.
  6. Tüm spesifikasyon sınıfları ve arayüzler javax.money. * Paketinde bulunur.

JSR 354 Örneği: Para ve Para Birimi API'sı:

Bir MonetaryAmount oluşturma ve konsola yazdırma örneği şuna benzer:

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

Referans uygulama API'sini kullanırken, gerekli kod çok daha basittir:

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

API ayrıca MonetaryAmounts ile hesaplamaları da destekler:

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

Para Birimi ve Parasal Miktar

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

MonetaryAmount, atanan para birimine, sayısal tutara, kesinliğine ve daha fazlasına erişmeyi sağlayan çeşitli yöntemlere sahiptir:

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;

MonetaryAmount'lar bir yuvarlama operatörü kullanılarak yuvarlanabilir:

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

MonetaryAmounts koleksiyonlarıyla çalışırken, filtreleme, sıralama ve gruplama için bazı güzel yardımcı yöntemler mevcuttur.

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

Özel Parasal Montaj işlemleri

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

Kaynaklar:

JSR 354 ile Java'da para ve para birimlerini işleme

Java 9 Para ve Para Birimi API'sini inceleme (JSR 354)

Ayrıca Bakınız: JSR 354 - Para Birimi ve Para


Tüm bunlar güzel, ama Federico yukarıda önerildiği gibi, BigDecimal'dan daha yavaş görünüyor :-)) o zaman sadece kötü şaka, ama şimdi 1 yıl sonra test vereceğim ...
kensai

6

Parasal değerleri temsil etmek için BigDecimal kullanmalısınız.Çeşitli yuvarlama modlarını kullanmanıza izin verir ve finansal uygulamalarda yuvarlama modu genellikle yasalarca zorunlu tutulabilecek zor bir gereksinimdir.



6

Moneta'yı (java para birimi JSR 354 uygulaması) BigDecimal ile performans açısından karşılaştırmak için bir mikrobenchmark (JMH) yaptım.

Şaşırtıcı bir şekilde, BigDecimal performansı moneta'nınkinden daha iyi görünüyor. Aşağıdaki moneta yapılandırma kullandım:

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

Sonuçlanan

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

Bir şey eksikse lütfen beni düzeltmekten çekinmeyin


İlginç, JDK9'da en son şeylerle aynı testi yapacağım
kensai

4

Basit bir durumda (bir para birimi) için yeterli it'is Integer/ Long. Parayı sent (...) veya yüzde / bininci sentte tutun (sabit bölücü ile ihtiyacınız olan herhangi bir hassasiyet)


3

BigDecimal, para birimi için kullanılacak en iyi veri türüdür.

Para birimi için çok sayıda kap vardır, ancak hepsi temel veri türü olarak BigDecimal kullanır. Muhtemelen BigDecimal.ROUND_HALF_EVEN yuvarlama kullanarak BigDecimal ile yanlış gitmeyeceksiniz.


2

Önceki cevapların önerdiği gibi, bir çift, BigDecimal veya int sarmak Tiny Türleri kullanmayı seviyorum . (Hassas sorunlar ortaya çıkmadıkça bir çift kullanırım).

Tiny Type, çift parayı diğer çiftlerle karıştırmamak için size tip güvenliği sağlar.


6
Ben de küçük türleri sevmekle birlikte, asla parasal bir değeri saklamak için bir çift kullanmamalısınız.
12'de orien
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.