Dinheiro e moedas com Java? Use a Java money API!

    Olá! Vou mostrar um pouco do funcionamento da nova API do Java, a “Money and Currency API”, criada sobre a JSR-354, prevista para ser lançada na versão 9 do Java.

    É bem comum na grande maioria dos sistemas a manipulação de valores monetários e o uso de cálculos que envolvam dinheiro como uma das funcionalidades principais.

    Desde a criação do Java 1.4, foi acrescentada a plataforma o suporte a diferentes tipos de moeda, através da classe “java.util.Currency” que tem o intuito de atender as especificações do “ISO-4217”, que padroniza a definição de códigos para as diferentes moedas existentes.

Algumas necessidades

    Mesmo que trabalhar com dinheiro seja algo comum, não existe nas versões do Java, alguma classe, API ou especificação que ofereça suporte ao conceito de “valor monetário”, sendo este algo que une uma quantidade de dinheiro (um “valor numérico” por ex.) a uma moeda específica (um “código da moeda” por ex.), e acaba sendo bem comum criarmos ou trabalharmos com este conceito, apenas pensando no “valor numérico”, quando estamos na verdade falando de “valor monetário”, afinal nós dizemos R$200,00 e USD 200.00 (duzentos reais e duzentos dólares, e sabemos também como devem ser escritos) e não apenas “duzentos”. De fato não é um grande problema se o seu software irá funcionar e trabalhar apenas com uma única moeda, mas outras necessidades surgem como por exemplo cálculo de arredondamento, e caso venha a utilizar outras moedas em seu sistema, é certo que você espera que uma conversão entre as diferentes moedas possa e passe a ocorrer.

    Com o intuito de endereçar e padronizar o tratamento de valores monetários na linguagem Java, é que a Java Money API foi criada, e trata desde a criação de classes para se trabalhar com o conceito de valor monetário, como atende a outras necessidades, sendo:

  • a criação um bom “tipo”, e oferecer uma boa forma de utilizá-lo em uma escala global para uso da linguagem, visto que trabalhar com moeda é algo comum.
  • ir além da classe “java.util.Currency”, que trata apenas os códigos de moedas e nada além.
  • as operações aritméticas com os valores monetários, e operações de arredondamento, seguindo o padrão das moedas.
  • o já mencionado, suporte a valor monetário (“MonetaryAmount”)
  • suportar a criação e uso de “moedas virtuais” como por ex.: BitCoin, Créditos de jogos online, etc.

Conhecendo a API

    As classes para se trabalhar com a API se encontram presentes no pacote “javax.money.*” e alguns subpacotes como “conversion”, “format” e “ext”.

    Vamos ver abaixo alguns exemplos das funcionalidades.

Moedas – CurrencyUnit

    Obtendo uma moeda específica, para isto você pode utilizar o código desta moeda, seguindo o “ISO-4217” ou um Locale específico:

// CurrencyUnit - Imutável e ThreadSafe.
// -> Obtendo uma moeda específica
CurrencyUnit dollar = MonetaryCurrencies.getCurrency("USD");
CurrencyUnit real = MonetaryCurrencies.getCurrency("BRL");

// -> Obtendo uma moeda, com base na localição - Locale.
CurrencyUnit euro = MonetaryCurrencies.getCurrency(Locale.FRANCE);
CurrencyUnit moedaPadrao = MonetaryCurrencies.getCurrency(Locale.getDefault());

    Cada moeda é definida pela classe “CurrencyUnit”, que é imutável e “Thread safe”.

Valores monetários – MonetaryAmount

    Para representar os valores monetários foi criada a classe “MonetaryAmount”, que seguindo a especificação, pode ser obtido através do uso de uma classe “Factory“, definida pela interface “MonetaryAmountFactory”, ou seja, é uma classe que faz a criação de do seu valor monetário com base em algumas informações passadas a ela, e lhe devolve a instância desejada.

    Uma forma simples de se criar os valores através desta fábrica seria:

// MonetaryAmount
// -> Obtido por uma Fábrica (Factory)
MonetaryAmount valorEmReais = MonetaryAmounts.getDefaultAmountFactory()
.setNumber(new BigDecimal("1500.55"))
.setCurrency("BRL")
.create();

    No exemplo acima é realizada a criação de R$1500,55, observe que para sua criação foi informado o valor desejado (BigDecimal) e a moeda “BRL” – reais. A classe “MonetaryAmount” irá centralizar toda as operações e cálculos envolvendo o valor em moeda.

    Através da Implementação de referência, é possível obter os valores de forma mais simples:

		// Algumas classes da implementação padrão podem ser usadas, por ex.:
		MonetaryAmount valorEmDollar = Money.of(new BigDecimal("100"), dollar);
		valorEmDollar = FastMoney.of(new BigDecimal("100"), dollar); 

    No exemplo acima o uso da classe “FastMoney” merece destaque e um ponto de atenção, pois internamente os valores desta implementação são armazenados através de campos tipo “long”, e permite que os cálculos sejam feitos de maneira mais rápida se comparados ao uso da classe “BigDecimal” que é utilizada internamento pela classe “Money” que é a implementação padrão.

    Alguns métodos e exemplos úteis para se trabalhar com “MonetaryAmount”, em diferentes situações :

// Obtendo o valor em BigDecimal:
BigDecimal valor = valorEmReais.getNumber().numberValue(BigDecimal.class);
System.out.println("Valor como BigDecimal: " + valor);
// Metodos úteis:
valorEmReais.isNegativeOrZero();
valorEmReais.isNegative();
valorEmReais.isPositiveOrZero();
valorEmReais.isPositive();
valorEmReais.isZero();
valorEmReais.abs();
valorEmReais.negate();
valorEmReais.isGreaterThan(valorEmReais);
valorEmReais.isLessThan(valorEmReais);

    Para trabalhar com operações aritméticas, pode se utilizar os métodos disponíveis na classe, sendo que cada operação irá devolver uma nova instância de valor monetário, por exemplo:

                // Operacoes com valores monetarios
		// -> Calculos
		valorEmReais.divide(2);
		valorEmReais.multiply(3);
                valorEmReais.subtract(valorEmReais);
		valorEmReais.add(valorEmReais);

    Se você tentar atribuir, ou somar valores que pertencem a moedas diferentes, uma exceção será lançada, pois este tipo de operação não pode ser realizada.

		// Somar Reais em Dólares não pode ser feito! Exception!
		//valorEmReais.add(valorEmDollar);

Operações monetárias – MonetaryOperator

    Se desejar criar as suas próprias operações monetárias, a API disponibiliza a Interface “MonetaryOperator”, que permite implementar a sua própria lógica ou sequência de cálculos sobre o “MonetaryAmount”, ao final devolve um novo “MonetaryAmount” como resultado dos cálculos.

    Para utilizar a sua operação, basta utilizar o método “with()” do “MonetaryAmount” que se deseja manipular.

    Um exemplo de operação de desconto (onde queremos retirar uma data porcentagem do valor total), seria:

    Usando Recurso de Lambda do Java 8:

double descontoEmPorcentagem = 2;
MonetaryOperator operacaoDeDesconto = (MonetaryAmount valorEmMoeda) -> valorEmMoeda.multiply(1 - (descontoEmPorcentagem/100));
MonetaryAmount comDesconto = valorEmReais.with(operacaoDeDesconto);
System.out.println("Valor com desconto: " + comDesconto);

    Criando a própria classe de desconto:

public static final class Discount implements MonetaryOperator{

private double percentage = 0;

private Discount(double percentage) {
setPorcentagem(percentage);
}

public static MonetaryOperator of(double percentage){
return new Discount(percentage);
}

private void setPorcentagem(double percentage){
this.percentage = percentage/100;
}

@Override
public MonetaryAmount apply(MonetaryAmount t) {
return percentage <= 0 ? t : t.multiply(1 - percentage);
}
}

    Usando a classe de desconto criada:

comDesconto = valorEmReais.with(Discount.of(descontoEmPorcentagem));
System.out.println("Minha própria classe de desconto:" + comDesconto);

Operações de arredondamento – MonetaryRounding

    Seguindo o uso da interface “MonetaryOperator” e da forma de trabalho das operações monetárias, foi criada a interface “MonetaryRounding” para auxiliar o trabalho com arredondamentos nas diferentes moedas existentes. Por extender “MonetaryOperator” a sua forma de utilização e possível aplicação são idênticas.

    Abaixo um exemplo de como realizar o arredondamento de um valor, seguindo a regra de arredondamento padrão:

MonetaryRounding arredondamento = MonetaryRoundings.getDefaultRounding();
valorEmReais = valorEmReais.with(arredondamento);
System.out.println("Valor em reais, com arredondamento padrão: " + valorEmReais);

Taxas de Câmbio e conversões – ExchangeRate , ExchangeRateProvider e CurrencyConversion

    Para trabalhar com as taxas de de Câmbio entre diferentes moedas a API define o uso de provedores, definidos pela interface “ExchangeRateProvider”.

    Obtendo informações dos provedores das taxas de câmbio:

// Provedor padrão (CompoundRateProvider)
ExchangeRateProvider provedorDeCambio = MonetaryConversions.getExchangeRateProvider();
// Taxa de Câmbio de Reais para Dóllar (usando o provider padrao)
ExchangeRate taxa = provedorDeCambio.getExchangeRate("BRL", "USD");
CurrencyUnit moedaAlvo = taxa.getCurrency();

// Obtendo um provedor de conversão específico
ExchangeRateProvider fmiProvedorDeCambio = MonetaryConversions.getExchangeRateProvider("IMF");

    O provedor padrão “CompoundRateProvider” delega a obtenção das taxas de Câmbio a outros provedores, uma cadeia de outros provedores e a taxa de Câmbio é definida pelo primeiro provedor que conseguir obter as informações.

    No exemplo acima, utilizamos as informações do provedor através do objeto “provedorDeCambio”, para encontrarmos a taxa de Câmbio entre reais(BRL) e dólares(USD) pelo uso do método “getExchangeRate()”. As taxas de câmbio são definidas pela classe “ExchangeRate”, que permite obter outras informações a respeito da taxa, como por exemplo a moeda alvo.

    Para conversão entre diferentes tipos de moeda, a API define a Interface “CurrencyConversion”, responsável por converter valores monetários entre duas moedas diferentes. Implicitamente, a conversão é realizada utilizando-se um ou mais provedores (ExchangeRateProvider). Um exemplo de conversão de Reais (BRL) para dólares (USD) seria:

                CurrencyConversion conversao = MonetaryConversions.getConversion("USD");
		MonetaryAmount reaisConvertidoParaDollar = valorEmReais.with(conversao);
		System.out.println("Valor convertido para dólares: " + reaisConvertidoParaDollar);

    A implementação de referência vem por padrão com dois provedores de conversão de moeda, que obtém as informações do “Banco Central Europeu” e o “Fundo Monetário Internacional” (FMI) (provedores “ECBCurrentRateProvider” e “IMFRateProvider”).

Formatando e exibindo valores – MonetaryAmountFormat

    Para exibição e formatação dos valores, a API define a interface “MonetaryAmountFormat”, responsável por formatar e apresentar os valores de acordo com um determinado “Locale”, tendo o formato, basta aplicar a ele o MonetaryAmount a ser formatado para exibição, exemplo:

MonetaryAmountFormat formatoPadrao = MonetaryFormats.getAmountFormat(Locale.getDefault());
String formatadoPadraoBR = formatoPadrao.format(valorEmReais);
System.out.println("Formato BR: " + formatadoPadraoBR);

    Também é possível através de uma informação, obter o valor monetário desejado, através de um processo de “parse”, por exemplo:

// Também é possível obter o valor com base na String de exibição do mesmo
		MonetaryAmount obtidoComFormatoBR = formatoPadrao.parse("BRL 1.500,55");
		System.out.println("Valor obtido com base na String de formato: " + obtidoComFormatoBR );

    Os exemplos aqui mencionados podem ser baixados através do GitHub – https://github.com/meduardo/exemplos-java-specs

    Estas são algumas das funcionalidades desta nova API, espero que tenham gostado e até a próxima.

    Links a respeito do assunto:

Por MARIO EDUARDO GIOLO

Postado em: 14 de setembro de 2015

Confira outros artigos do nosso blog

REST não é JSON

21 de agosto de 2017

Bruno Sofiato

[Webinar] Profile de aplicações Java com Oracle Mission Control e Flight Recorder

24 de julho de 2017

Danival Calegari

Criando Mocks de serviços REST com SoapUI

27 de junho de 2017

Monise Costa

JavaScript 6: diferença entre var, let e const

09 de maio de 2017

Otávio Felipe do Prado

Deixe seu comentário