(Java)Money and Kotlin
Tags: kotlin, money, java, jsr354
Published: 2021-04-17 14:55:57

The [JavaMoney API](https://javamoney.github.io/) is part of [JSR 354](https://jcp.org/en/jsr/detail?id=354) and will (hopefully) be part of the Java language in the future. The reference implementation [Moneta](https://github.com/JavaMoney/jsr354-ri) provides good examples of implementations using the JavaMoney API.

The JavaMoney API allows working with *currencies*, *monetary values* and *conversion* between monetary values of different currencies through *exchange rates*. View the [Devoxx video](https://www.youtube.com/watch?v=heH08CROmb0) which explains the API in depth.

Kotlin is interoperable with compiled Java libraries. So the JavaMoney/Moneta library can be used from Kotlin code, and makes working with money in business logic easy. After all, money seems trivial but details like rounding, conversion and catalogues of locales and currencies are hard to implement correctly.

The JavaMoney API is very flexible and extendable. The classes and interfaces provide a Builder pattern to contruct instances with fluent code. For example

```java
// Construct a monetary value of €200 using the FastMoney implementation
Monetary.getAmountFactory(FastMoney.class)
        .setCurrencyUnit("EUR")
        .setNumber(200)
        .setContext(MonetaryContextBuilder.of().set(MathContext.DECIMAL128).build())
        .create();
```

The builder pattern does not look like ideomatic Kotlin. In Kotlin, [builder DSLs](https://kotlinlang.org/docs/type-safe-builders.html) are used instead to fluently construct complex objects.

That is exactly what I implemented in [Money-Kotlin](https://github.com/hiddewie/money-kotlin).

## Money-Kotlin

The [Money-Kotlin](https://github.com/hiddewie/money-kotlin) library extends the JavaMoney API with Kotlin extension functions.

The example above could be rewritten in Kotlin as

```kotlin
// Construct a monetary value of €200 using the FastMoney implementation
200.ofCurrency<FastMoney>("EUR", monetaryContext {
	set(MathContext.DECIMAL128)
})
```

More details are given below. Some of the (Java) examples are taken from the [Moneta User Guide](https://github.com/JavaMoney/jsr354-ri/blob/master/moneta-core/src/main/asciidoc/userguide.adoc).

### Monetary amounts

The first example displayed above shows how to construct monetary values. Often we do not need a monetary context.

Java:  

```java
Monetary.getAmountFactory(FastMoney.class)
        .setCurrencyUnit("EUR")
        .setNumber(200.01)
        .create();
```

Kotlin:

```kotlin
val money = (200.01).ofCurrency<FastMoney>("EUR")
```

We can manipulate money amounts as well, just like regular numbers. This has been implemented using Kotlin operator overloading.

Java:

```java
MonetaryAmount money = ...;

// add
money.add(money);
money.plus();

// subtract & negate
money.subtract(money);
money.negate();

// multiply
money.multiply(2.0);

// divide & remainder
money.divide(2.0);
money.remainder(2.0);
```

Kotlin:

```kotlin
// add
money + money
+money

// subtract & negate
money - money
-money

// multiply
money * 2.0
2.0 * money

// divide & remainder
money / 2.0
money % 2.0
```

### Currencies

You can access a default or self-implemented currency (like Bitcoin) from the `Monetary` singleton.

Java:

```java
CurrencyUnit currencyEUR = Monetary.getCurrency("EUR");
```

Kotlin:

```kotlin
"EUR".asCurrency()
```

### Locales

The JavaMoney API associates a locale with a currency. For example, in The Netherlands the official currency is the Euro. Some countries have multiple currencies.

Java:

```java
new Locale("", "NL");
Monetary.getCurrency(new Locale("", "NL"));
Monetary.getCurrencies(new Locale("", "NL"));
```

Kotlin:

```kotlin
"NL".asCountryLocale()
"NL".asCountryLocale().getCurrency()
"NL".asCountryLocale().getCurrencies()
```

### Rounding

A monetary value can be rounded in a specific way. Construct a rounding query to get a `MonetaryRounding`.

Java:

```java
Monetary.getRounding(
	RoundingQueryBuilder
		.of()
		.setScale(4)
		.set(RoundingMode.HALF_UP)
		.build()
	);
```

Kotlin:

```kotlin
Monetary.getRounding(roundingQuery {
	setScale(4)
	set(RoundingMode.HALF_UP)
})
```

### Currency conversion

A `MonetaryAmount` can be converted from one currency to another currency through a `CurrencyConversion`. A `CurrencyConversion` depends on a conversion provider. There are some default providers implemented out of the box, like the *European Central Bank* (ECB) and *International Monetary Fund* (IMF). These providers provide currency exchange rates, in particular historic rates.

The order of the default providers are configurable, and you can implement your own conversion provider as well.

Java:

```java
ExchangeRateProvider rateProvider = MonetaryConversions.getExchangeRateProvider("ECB", "IMF");

MonetaryAmount amountInEUR = ...;
MonetaryAmount amountInCHF = amountInEUR.with(rateProvider.getExchangeRate("EUR", "CHF"));

// Using the default rate providers
MonetaryAmount amountInCHF = amountInEUR.with(MonetaryConversions.getExchangeRateProvider().getExchangeRate("EUR", "CHF"));
```

Kotlin: 

```kotlin
val rateProvider = MonetaryConversions.getExchangeRateProvider("ECB", "IMF");

val amountInEUR = ...
val amountInCHF = amountInEUR.convertTo("CHF", rateProvider)

// Using the default rate providers
val amountInCHF = amountInEUR.convertTo("CHF")
```

### Formatting

Monetary amounts are formatted in the context of a locale.

Java:

```java
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.GERMANY);
MonetaryAmount amount = ...;
format.format(amount);
```

Kotlin:

```kotlin
val amount = ...
val format = Locale.GERMANY.monetaryAmountFormat()
format.format(amount);
```

## Getting started

You can use the Money-Kotlin library today! It has been published on [Maven Central](https://search.maven.org/artifact/nl.hiddewieringa/money-kotlin/), and can be added as a dependency.

There are many more examples in the [project readme](https://github.com/hiddewie/money-kotlin/#readme).

Gradle:

```kotlin
implementation("nl.hiddewieringa:money-kotlin:$moneyKotlinVersion")
```

Maven:

```xml
<dependency>
  <groupId>nl.hiddewieringa</groupId>
  <artifactId>money-kotlin</artifactId>
  <version>${moneyKotlin.version}</version>
</dependency>
```

[Feedback, feature requests and bug reports](https://github.com/hiddewie/money-kotlin) are welcome!