JSR 354: API para trabajar con valores monetarios en aplicaciones Java

Por Alexis Lopez
Publicado en Junio 2015

Esta nueva API nos permitirá trabajar con monedas y valores monetarios de una manera más completa, significativa y extensible en nuestras aplicaciones Java. Aunque está basada en las nuevas características introducidas en Java SE 8, también se ofrece una versión compatible con Java SE 7 y se busca que también pueda ser usada en ambientes Java EE y Java ME.

En este artículo describiremos las partes más importantes de esta nueva API y realizaremos algunos ejercicios para familiarizarnos con las funcionalidades que provee. No entraremos en el detalle de cada clase/interface/método pero sí proveeremos enlaces para profundizar en el tema.

Nota importante: Este artículo se escribe teniendo como base el documento “JavaMoney_Specification_1.0-final” (www.jcp.org/en/jsr/detail?id=354) que, a la fecha de escritura de este artículo, es el documento más reciente de la especificación. Sin embargo, se advierte que es posible que algunas características de la especificación cambien una vez se haya concluido su desarrollo.

Definiciones
Antes de iniciar, debemos tener claro los siguientes conceptos:

  1. Application Programming Interface (API): Es la descripción de clases/interfaces/métodos... que se usan e invocan para alcanzar un objetivo. Te dice qué hace cada clase/interface/método... Los usuarios generalmente usan los componentes de una API no de un SPI.
  2. Service Provider Interface (SPI): Es la descripción de clases/interfaces/métodos... que se heredan e implementan de una API, para alcanzar un objetivo. Te dice cómo hacer lo especificado en la API para cada clase/interface/método... Los usuarios de una API pocas veces requieren ver o usar los componentes de un SPI.
  3. Java Community Process (JCP): Desde 1998, es un proceso transparente y participativo para el desarrollo y revisión de especificaciones de la tecnología Java, implementaciones de referencia y sus pruebas. El JCP fomenta la evolución de la plataforma Java en cooperación con la comunidad internacional de desarrolladores de Java.
  4. Java Specification Request (JSR): Permite proponer el desarrollo de una nueva especificación o revisión significativa de una especificación existente de la tecnología Java. Entre sus entregables se encuentran:
    1. Documento de la especificación: Detalla el alcance, escenarios, estructura, etc. De la API en cuestión.
    2. Implementación de referencia: Implementación que demuestra que la especificación puede ser implementada.
    3. Equipo de compatibilidad tecnológica (TCK): Conjunto de pruebas que verifican que una implementación es totalmente compatible con la especificación.

Introducción
A continuación se cita un apartado (traducido) del documento de esta especificación:

Los valores monetarios son parte fundamental de muchas aplicaciones, sin embargo, el JDK provee poco o ningún soporte para éstos.

La clase existente java.util.Currency es solamente una estructura para la representación de las monedas actuales (ISO-4217), pero no para representar sus valores asociados o monedas personalizadas. El JDK tampoco ofrece soporte para operaciones aritméticas con monedas o conversión entre ellas ni tampoco un tipo de valor estándar para representar un valor monetario.

De lo anterior, podemos inferir que los objetivos de esta API son:

  1. Proveer un API para el manejo de monedas y dinero en Java.
  2. Soportar el estándar ISO-4217 y monedas personalizadas (por ejemplo virtuales: BitCoin, Facebook Credits, Linden Dollar...), así como la representación de valores monetarios.
  3. Ofrecer operaciones aritméticas entre valores monetarios, incluso de diferentes monedas, y conversión entre éstos.
  4. Permitir el formato y conversión de valores monetarios hacia/desde cadenas de texto. Este formato debe ser flexible, soportar diferentes estilos y posición de la moneda, además de permitir diferentes tamaños para el agrupamiento de los números.
  5. Proveer un conjunto de operaciones de redondeo así como soportar redondeos personalizados.

 

Lo cual supone los siguientes retos:

  1. Que sea una API simple, pero aplicable a la mayor cantidad de casos posibles.
  2. Mantener un rendimiento óptimo que permita usarlo en aplicaciones de alto desempeño como por ejemplo aplicaciones de comercio electrónico.
  3. Que se pueda extender, dado que existen diferencias en cuanto a la precisión, conversión y formato de los valores monetarios. Ejemplos:
    1. El redondeo de valores monetarios en países como Suiza y Argentina es diferente a como se hace en otros países.
    2. Los formatos de valores monetarios también son diferentes dependiendo del país. Por ejemplo, el formato de la Rupia India es algo así: 12,34,00,000.
  4. Debe ser posible usar el JSR sin requerir acceso a recursos externos, por ejemplo, sin tener que estar conectados a Internet. Aunque esto puede limitar las funcionalidades de conversión entre monedas.

 

Algo que vale la pena mencionar es que se ha tenido en cuenta que algunas de las clases e interfaces principales sean inmutables, serializables y seguras para concurrencia. Además que, en lo posible, se siga la nomenclatura de nombres y estilos encontrados en la API de colecciones o en la de fechas y horas (java.time).

Estructura
Las clases e interfaces que conforman esta API se encuentran agrupadas en cuatro paquetes. A continuación se detallan las más relevantes:

javax.money: Paquete principal de la especificación. Contiene clases e interfaces que representan monedas y valores monetarios, algunas excepciones y utilitarios para el redondeo y extensión. A continuación las más importantes:

  1. CurrencyUnit: Modela las propiedades de una moneda: su código (USD, EUR, COP...), cantidad dígitos fraccionales por defecto, etc. Tanto para monedas de la norma ISO-4217 como para monedas personalizadas (virtuales, históricas, etc.).
  2. MonetaryAmount: Define un valor monetario. Se encuentra formado por un valor (javax.money.NumberValue) que se puede obtener por medio de +getNumber():NumberValue y una moneda (javax.money.CurrencyUnit) que se puede obtener por medio de  +getCurency():CurrencyUnit.
  3. MonetaryOperator y MonetaryQuery: Definen los puntos de extensión de esta API, permitiendo la implementación de funcionalidades adicionales. El primero se puede usar para adicionar operaciones que retornen un valor monetario (javax.money.MonetaryAmount) mientras que el segundo para retornar algún otro tipo de valor.
  4. MonetaryRounding: Permite la implementación de diferentes redondeos que pueden extenderse más allá de los redondeos aritmeticos. Es importante, dado que en el área financiera se pueden presentar variaciones no estándar de redondeos.
  5. MonetaryContext: Provee las capacidades numéricas que tiene un valor monetario: la precisión, la escala, la clase numérica que representa su valor y otros atributos.
  6. MonetaryException: Excepción base para esta API, hereda de java.lang.RuntimeException.
  7. Monetary: Clase singleton que provee acceso a instancias de tipo javax.money.CurrencyUnit, javax.money.MonetaryAmount y javax.money.MonetaryRounding.

javax.money.format: Paquete que contiene clases, interfaces y algunas excepciones para trabajar con formatos de valores monetarios. A continuación las más importantes:

  1. MontaryAmountFormat: Realiza el formato de valores monetarios a cadenas de texto y desde cadenas de texto (parsing). Es importante mencionar que las instancias de este tipo no son seguras para concurrencia y se recomienda su uso sincronizado.
  2. MonetaryFormats: Singleton que permite acceder a instancias de javax.money.format.MonetaryAmountFormat a partir de localizaciones o a partir de instancias de tipo javax.money.format.AmountFormatQuery.
  3. AmountFormatQuery: Permite pasar una serie de atributos/parámetros para la configuración de instancias del tipo javax.money.format.MonetaryAmountFormat lo que da flexibilidad al formato de valores monetarios ofrecidos por las implementaciones del API. Es responsabilidad de la implementación interpretar/ignorar dichos atributos/parámetros.
  4. MonetaryParseException: Hereda de MonetaryException y es lanzada cuando no se ha podido convertir una cadena de texto a un valor monetario correctamente. Permite obtener la cadena original que se quería convertir y el índice en la cadena donde falló la conversión.

javax.money.convert: Paquete que contiene clases e interfaces para trabajar con tasas de cambio y conversión de valores monetarios. A continuación las más importantes:

  1. MonetaryConversions: Singleton que provee acceso a diferentes aspectos relacionados con la conversión: Proveedores que ofrecen las tasas de cambio y su información relacionada, acceso a operadores de conversión (javax.money.convert.CurrencyConversion) para usarlos en valores monetarios, etc.
  2. CurrencyConversion: Modela la conversión de valores monetarios. Esta interface hereda de javax.money.MonetaryOperator lo cuál permite que sea usada en cualquier valor monetario. También permite obtener la tasa de cambio usada para la conversión.
  3. ExchangeRate: Permite obtener detalles de la conversión: Monedas origen y destino, la tasa, entre otros.
  4. ConversionContext: Permite el paso de atributos/parámetros adicionales que puedan requerir los proveedores de tasas de cambio. Con esto se logra soportar casos complejos de conversión en dónde el proveedor requiera más información que las monedas origen/destino y la fecha. Es responsabilidad del proveedor interpretar/ignorar dichos atributos/parámetros adicionales.
  5. ConversionQuery: Igual que javax.money.convert.ConversionContext, pero su uso va enfocado hacia obtener instancias de javax.money.convert.CurrencyConversion que cumplan con los atributos/parámetros establecidos. Es responsabilidad del proveedor interpretar/ignorar dichos atributos/parámetros adicionales.

Es importante mencionar que existe un concepto llamado “cadena de proveedores”, el cual es un lista de proveedores de tasas de cambio. El valor de una tasa de cambio requerida, será la del primer proveedor de la “cadena de proveedores” que retorne un valor no-nulo.

javax.money.spi: En este paquete se definen clases/interfaces/métodos que deben implementar las  implementaciones (valga la redundancia) de esta API para ofrecer las funcionalidades descritas por la especificación. En este artículo nos enfocaremos en las funcionalidades más no en cómo crear una implementación, por lo que no explicaremos el contenido de este paquete. Para mayor información, la especificación (https://jcp.org/en/jsr/detail?id=354) tiene muy bien documentada esta parte.

Implementación de Referencia
Uno de los entregables de todo JSR es la implementación de referencia (RI por sus siglas en inglés) del JSR. Para esta especificación se ha creado una implementación de referencia llamada Moneta (http://javamoney.github.io/ri.html) y en los siguientes ejemplos haremos uso de esta implementación para poder probar las funcionalidades que nos afrece la API de Monedas y Dinero.

Para poder usar la implementación, adiciona la siguiente dependencia a tu archivo pom.xml

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

Si no estás usando MAVEN, puedes acceder al código fuente en GitHub (https://github.com/JavaMoney/jsr354-ri) o desde Maven Central (http://search.maven.org/#search%7Cga%7C1%7Cmoneta) y generar un archivo .jar que luego puedes usar en tu proyecto.

Ejemplos
A continuación presentamos algunos ejemplos de las funcionalidades básicas que nos ofrece esta nueva API. Todos están basados todos en la implementación de referencia, Moneta, para otras implementaciones (en el futuro) los ejemplos pueden variar.

Monedas
Vamos a iniciar con las monedas. Existen tres formas de obtener una moneda: la primera por medio del código asignado a la moneda según ISO-4217, la segunda por medio de una instancia de java.util.Locale y la tercera es creando una moneda personalizada y registrandola para su posterior uso:

CurrencyUnit  pesosCop = Monetary.getCurrency("COP"); 
   CurrencyUnit  pesosColombia = Monetary.getCurrency(new Locale("es",  "CO")); 
CurrencyUnit  usDolars = Monetary.getCurrency(Locale.US); 
//Así podemos  crear una moneda personalizada y registrarla para su posterior uso
CurrencyUnit  moneda = CurrencyUnitBuilder.of("XBT", "default") 
                                          .setNumericCode(-1)
                                          .setDefaultFractionDigits(3)
                                          .build();

 

Veamos algunas propiedades de la moneda creada anteriormente:

System.out.printf("Cod  = %s\n", pesosCop.getCurrencyCode()); 
System.out.printf("Cod  Num = %d\n", pesosCop.getNumericCode()); 
System.out.printf("Dígitos  fracc = %d\n", pesosCop.getDefaultFractionDigits()); 
**Salida**
Cod = COP
Cod Num = 170
Dígitos fracc = 2

 

El API, además, especifica que las implementaciones de javax.money.CurrencyUnit deben implementar equals/hashCode, ser comparables, inmutables, seguras para concurrencia y serializables.

Valores Monetarios
Ahora que ya tenemos una moneda, vamos a crear valores monetarios. Para ello podemos usar las referencias a las monedas creadas previamente o usar nuevamente el código de la moneda. Notice the use of the javax.money.Monetary singleton:

CurrencyUnit  dolarUS = ... 
MonetaryAmount  valorCop = Monetary.getDefaultAmountFactory() 
                                   .setCurrency("COP") 
                                   .setNumber(500_000).create(); 
MonetaryAmount  valorUsd = Monetary.getDefaultAmountFactory() 
                                   .setCurrency(dolarUS)
                                   .setNumber(500.55)
                                   .create();

 

Veamos algunas propiedades del valor monetario recién creado:

System.out.printf("Moneda  = %s\n", valorUsd.getCurrency()); 
System.out.printf("Cantidad  = %s\n", valorUsd.getNumber()); 
System.out.printf("Precisión  = %d\n", valorUsd.getNumber().getPrecision()); 
System.out.printf("Escala  = %d\n", valorUsd.getNumber().getScale()); 
System.out.printf("Numerador  fracción = %d\n",
valorUsd.getNumber().getAmountFractionNumerator());
System.out.printf("Denominador  fracción = %d\n",
valorUsd.getNumber().getAmountFractionDenominator());
 
**Salida**
Moneda = USD
Cantidad = 500.55
Precisión  = 5 → 5 dígitos
Escala  = 2 → 2 decimales
Numerador  fracción = 55 → El número decimal
Denominador  fracción = 100 → Dado que los decimales son centavos

 

El API, además, especifica que las implementaciones de javax.money.MonetaryAmount deben implementar equals/hashCode (los cuales tienen en cuenta el tipo de implementación del valor monetario), ser comparables, inmutables, seguras para concurrencia, serializables y ser declaradas como final. Teniendo en cuenta que equals/hashCode deben tener en cuenta el tipo de la implementación del valor monetario, dos implementaciones diferentes de un mismo valor monetario y la misma moneda son considerados diferentes. Para compararlos teniendo en cuenta únicamente la moneda y su valor monetario se debe usar el método +isEqualTo(MonetaryAmount):boolean

Operaciones Aritméticas
Las operaciones aritméticas son similares a las encontradas en la clase java.math.BigDecimal y deben realizarse entre valores monetarios que compartan la misma moneda, de lo contrario, obtendríamos una excepción. Por ejemplo, tratemos de sumar nuestros valores monetarios definidos previamente:

… 
MonetaryAmount  valorCop = … 
MonetaryAmount  valorUsd = … 
valorUsd.add(valorCop); 
…
El anterior bloque de código lanzaría la  siguiente excepción:
javax.money.MonetaryException:  Currency mismatch: USD/COP 

 

Las operaciones artiméticas pueden ser encadenadas para lograr un valor final:

… 
MonetaryAmount  valorCop = … 
MonetaryAmount  valorCop100 = Monetary.getDefaultAmountFactory() 
.setCurrency("COP") 
.setNumber(100_000) 
.create(); 
MonetaryAmount valorCopFinal =  valorCop.subtract(valorCop100) 
                                        .multiply(2)
                                        .add(valorCop100);
System.out.printf("Valor Final =  %s\n", valorCopFinal);
…
**Salida**
Valor Final = COP 900000

 

Extensión
Podemos crear instancias de la interface funcional javax.money.MonetaryOperator para aplicar operaciones sobre valores monetarios. Por ejemplo, por medio del siguiente código podemos obtener un valor monetario que es el doble del valor monetario sobre el cual usamos el operador:

…
MonetaryAmount valorCop = …
MonetaryOperator duplicador = v ->  v.multiply(2);
System.out.printf("Valor Duplicado  = %s\n", valorCop.with(duplicador));
…
**Salida**
 Valor Duplicado = COP 1000000

 

Nótese que definimos la instancia de javax.money.MonetaryOperator como una expresión lambda. Esto es posible, obviamente porque estamos usando Java SE 8 y porque dicha interface es una interface funcional equivalente a java.util.function.UnaryOperator la cual describe una función que retorna un resultado del mismo tipo de su operando. Por medio de este mecanismo se hace posible extender las funcionalidades de esta API, podríamos crear operadores para el cálculo de operaciones financieras como el valor presente, interés compuesto, etc. Y aplicarlo a nuestros valores monetarios.

Por otro lado, podríamos crear instancias de la interface funcional javax.money.MonetaryQuery para realizar consultas sobre valores monetarios y retornar algún otro valor (no tiene que ser otro valor monetario). Por ejemplo, por medio del siguiente código podemos saber si el valor monetario sobre el cual usamos el query es positivo:

… 
MonetaryAmount  valorCop = … 
MonetaryQuery  positivoQuery = v -> v.isPositive(); 
System.out.printf("Valor  = %s\n", valorCop); 
System.out.printf("Valor positivo?  = %s\n", valorCop.query(positivoQuery));
…
**Salida**
Valor = COP 500000
Valor positivo? = true

 

Nótese que definimos la instancia de javax.money.MonetaryQuery como una expresión lambda. Esto es posible, obviamente porque estamos usando Java SE 8 y porque dicha interface es una interface funcional equivalente a java.util.function.Function la cual describe una función que recibe un parámetro y retorna otro resultado.

Redondeo
Para redondear valores monetarios se sigue el mismo mecanismo que con los operadores monetarios, se debe obtener una instancia de javax.monet.MonetaryRounding y aplicarla al valor monetario:

… 
MonetaryAmount  valor = Monetary.getDefaultAmountFactory().setCurrency("COP") 
.setNumber(500_000.3472).create(); 
MonetaryRounding  roundingCop = Monetary.getRounding(Monetary.getCurrency("COP")); 
System.out.printf("COP  redondeado = %s\n", valor.with(roundingCop)); 
…
**Salida**
COP redondeado = COP 500000.35
 

El redondeo se basa en los dígitos decimales por defecto que tiene configurada la moneda sobre la cual se está aplicando dicha operación. Si se desea obtener un redondeo diferente, y este es proveído por la implementación, se puede crear una instancia de javax.money.RoundingQuery y pasar parámetros para que la implementación pueda retornar el redondeo más adecuado, por ejemplo, si la implementación tuviera un redondeo llamado “cashRounding” para la moneda Francos Suizos (CHF), podríamos acceder a él usando el siguiente código:

MonetaryRounding  rounding = Monetary.getRounding( 
                  RoundingQueryBuilder.of() 
                                      .setRoundingName("cashRounding") 
                                      .setCurrency(Monetary.getCurrency("CHF")) 
                                      .build());

 

Formato
A continuación veremos un ejemplo de formato de valores monetarios:

… 
MonetaryAmount  valorUsd = … 
MonetaryAmountFormat  formato = MonetaryFormats.getAmountFormat(Locale.US); 
MonetaryAmountFormat  formPersonalizado = MonetaryFormats.getAmountFormat( 
AmountFormatQueryBuilder.of(Locale.US) 
                        .set("pattern",  "$###.## ¤;($###.##) ¤")
                        .build() 
System.out.printf("Formato  = %s\n", formato.format(valorUsd)); 
System.out.printf("Formato  Personalizado = %s\n",
                            formatoPersonalizado.format(valorUsd));
…
**Salida**
Formato = USD500.55
Formato Personalizado = $500.55 USD

 

De lo anterior podemos notar que existen formatos predefinidos, pero que también podemos definir formatos personalizados. La forma de obtener formatos personalizados es usando una instancia de javax.money.format.AmountFormatQuery en donde definimos las propiedades del formato deseado, pero depende de la implementación de la API el saber que hacer con las propiedades pasadas en dicha instancia. Un buen ejemplo de porqué lo anterior es buena práctica es el del formato para la moneda India (INR) en donde podemos pasar el tamaño que deseamos para el agrupamiento de los números:

… 
MonetaryAmount  valorInr = Monetary.getDefaultAmountFactory() 
                                   .setCurrency("INR") 
                                   .setNumber(123456789101112.123456) 
                                   .create(); 
AmountFormatQuery  query = AmountFormatQueryBuilder.of(new Locale("", "INR")) 
                                                   .set("groupingSizes", new int[]{3, 2}) 
                                                   .build(); 
MonetaryAmountFormat  formatINR = MonetaryFormats.getAmountFormat(query); 
System.out.printf("Formato  INR = %s\n", formatINR.format(valorInr)); 
…
**Salida**
Formato INR = INR  12,34,56,78,91,01,112.12

 

javax.money.format.MonetaryAmountFormat también ofrece un método para convertir de cadena de texto a valor monetario (parse).

Conversión
Ahora veremos algunos ejemplos de conversión entre monedas. La implementación de referencia, Moneta, utiliza los siguientes proveedores (en ese orden por defecto) para obtener las tasas de cambio: Banco Central Europeo (ECB), Fondo Monetario Internacional (IMF) y un histórico del Banco Central Europeo (ECB-HIS). Otras implementaciones pueden usar otros proveedores, ya que esto depende de la implementación.

MonetaryAmount  valorMxn = Monetary.getDefaultAmountFactory() 
                                   .setCurrency("MXN") 
                                   .setNumber(500).create();  
CurrencyConversion  conversion = MonetaryConversions.getConversion("USD"); 
MonetaryAmount  valorEnDolares = valorMxn.with(conversion); 
System.out.printf("En  dólares = %s\n", valorEnDolares); 
System.out.printf("Tasa  = %s\n", conversion.getExchangeRate(valorMxn) 
                                            .getFactor()); 
System.out.printf("Proveedor  = %s\n", conversion.getExchangeRate(valorMxn) 
                                                 .getContext()
                                                 .getProviderName());
**Salida**
En dólares = USD 32.56487742900737956
Tasa = 0.06512975485801475912
Proveedor = ECB

 

Podemos ver que la conversión utiliza el mismo mecanismo usado al aplicar un operador sobre un valor monetario, también podemos notar que de la conversión podemos obtener más información, por ejemplo, la tasa, el proveedor, etc.

Al igual que con el formato de valores monetarios, es posible solicitar conversiones más personalizadas pasando parámetros como fechas, clientes, etc. Por medio del uso de instancias de javax.money.convert.ConversionQuery:

MonetaryAmount  valorMxn = Monetary.getDefaultAmountFactory() 
                                   .setCurrency("MXN") 
                                   .setNumber(500).create(); 
ConversionQuery  query = ConversionQueryBuilder.of() 
                                   .setRateTypes(RateType.DEFERRED) 
                                   .set("customerID", 1234) 
                                   .set("contractID",  "ABC") 
                                   .setTermCurrency("CHF") 
                                   .build(); 
CurrencyConversion  conversion = MonetaryConversions.getConversion(query); 
MonetaryAmount  valorEnFrancos = valorMxn.with(conversion); 
System.out.printf("En  Francos = %s\n", valorEnFrancos); 
System.out.printf("Tasa  = %s\n", conversion.getExchangeRate(valorMxn) 
                                            .getFactor()); 
System.out.printf("Proveedor  = %s\n", conversion.getExchangeRate(valorMxn) 
                                            .getContext()
                                            .getProviderName());
**Salida**
En Francos = CHF 30.697666134308597268
Tasa = 0.061395332268617194536
Proveedor = ECB

 

De esta forma podemos pasar parámetros para que la implementación pueda retornar una tasa más personalizada. Sin embargo, es responsabilidad de la implementación el interpretar dichos parámetros.

Conclusión
Gracias a esta API será posible trabajar con valores monetarios en nuestras aplicaciones Java de una manera más fácil y significativa. Repasamos las principales caracteristicas del API entre las que se encuentran:

  • Monedas: Obtuvimos monedas actuales y vimos la creación de monedas personalizadas
  • Valores monetarios: Creamos valores monetarios y realizamos operaciones aritméticas entre éstos
  • Formato: Aplicamos diferentes formatos a valores monetarios de diferentes monedas y pudimos observar que la API permite personalizar dichos formatos
  • Redondeo: Ejecutamos el redondeo de valores monetarios usando los redondeos por defecto y vimos que es posible solicitar redondeos más personalizados si la implementación lo soporta
  • Conversión: Realizamos la conversión entre valores monetarios de diferentes monedas y obtuvimos información adicional de dicha conversión
  • Extensión: Observamos que es posible extender la API por medio de clases funcionales como javax.money.MonetaryOperator y javax.money.MonetaryQuery para adicionar nuevas funcionalidades.

Existen otras características y funcionalidades no cubiertas en este artículo, las cuales podemos consultar con más detalle en el documento de la especificación. Este JSR ya se encuentra en su versión final, esperamos contar con esta API en futuras versiones de Java.

Puedes encontrar todos los ejemplos usados en este artículo en GitHub: https://github.com/aalopez/jsr354

Información Adicional
Los siguientes enlaces ofrecen mayor información respecto a esta API:

Página Oficial del JSR
https://www.jcp.org/en/jsr/detail?id=354

Código fuente, documentación
http://javamoney.github.io/

Proyecto en Java.net
https://java.net/projects/javamoney/pages/Home



Alexis Lopez (@aa_lopez) es consultor independiente con 8+ años de experiencia usando tecnologías Java/Oracle para la creación de sistemas de información. Ha sido profesor universitario de cursos relacionados con Java y conferencista en congresos reconocidos como: Oracle Open World, JavaOne y Campus Party. Cuenta con un título de ingeniero de sistemas y las siguientes certificaciones: SCJP, OCPJMAD, OCPWCD, especialista de implementación de Oracle ADF y Oracle BPM. Es líder del grupo de usuarios Java de Cali-Colombia (www.clojug.org) y blogger activo en www.java-n-me.com

Este artículo ha sido revisado por el equipo de productos Oracle y se encuentra en cumplimiento de las normas y prácticas para el uso de los productos Oracle.