NumberFormat.getCurrencyInstance: примеры (JAVA)

Форматирование валюты в Java с NumberFormat.getCurrencyInstance
Раздел: Форматирование чисел и денежных единиц (NumberFormat)
NumberFormat.getCurrencyInstance: NumberFormat

Общее описание

Метод NumberFormat.getCurrencyInstance из пакета java.text создает объект форматирования чисел, настроенный для представления денежных сумм в соответствии с локалью. Доступны две перегрузки: без аргументов и с аргументом Locale. Первая версия возвращает формат для текущей системной локали, вторая - для указанной.

Возвращаемый тип: NumberFormat. На большинстве платформ экземпляр будет подклассом DecimalFormat, что позволяет дополнительно настраивать шаблон, символы и поведение парсинга.

Ключевые правила работы:

  • Локаль определяет символ валюты, разделители групп и десятичный разделитель.
  • По умолчанию число знаков после запятой соответствует Currency.getDefaultFractionDigits() для валюты локали.
  • Формат не является потокобезопасным: один экземпляр не должен использоваться одновременно из нескольких потоков без синхронизации.

Аргументы и возвращаемые значения:

  • getCurrencyInstance() - нет аргументов. Возвращает NumberFormat для системной локали.
  • getCurrencyInstance(Locale locale) - аргумент locale типа java.util.Locale. Если locale равен null, возможен NullPointerException (в зависимости от реализации JVM).
  • Возвращаемый объект предоставляет методы format(Number) для форматирования и parse(String) для разбора. Для тонкой настройки можно применять методы setCurrency(Currency), setMinimumFractionDigits, setMaximumFractionDigits, setGroupingUsed и при необходимости приводить к DecimalFormat для доступа к дополнительным настройкам.

Замечание:

Для финансовых вычислений предпочтительнее хранить деньги в типах с фиксированной точкой (например, BigDecimal или специализированных API), а NumberFormat использовать только для отображения и разбора представлений.

Простые примеры

Пример 1: Форматирование по явной локали

import java.text.NumberFormat;
import java.util.Locale;

public class Example1 {
    public static void main(String[] args) {
        NumberFormat nf = NumberFormat.getCurrencyInstance(Locale.US);
        System.out.println(nf.format(12345.67));
    }
}
$12,345.67

Пример 2: Форматирование для Франции

NumberFormat nf = NumberFormat.getCurrencyInstance(Locale.FRANCE);
System.out.println(nf.format(12345.67));
12 345,67 €

Пример 3: Изменение валюты у возвращенного формата

import java.text.NumberFormat;
import java.util.Currency;
import java.util.Locale;

NumberFormat nf = NumberFormat.getCurrencyInstance(Locale.US);
nf.setCurrency(Currency.getInstance("JPY"));
System.out.println(nf.format(12345.67));
¥12,346

Пример 4: Разбор строки в число

import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;

NumberFormat nf = NumberFormat.getCurrencyInstance(Locale.US);
try {
    Number n = nf.parse("$1,234.56");
    System.out.println(n); // будет Double или Long в зависимости от реализации
} catch (ParseException e) {
    e.printStackTrace();
}
1234.56

Аналоги в Java

  • DecimalFormat - более низкоуровневый класс, дающий доступ к шаблонам, DecimalFormatSymbols и дополнительным настройкам. Предпочтителен, когда требуется точная настройка шаблона отображения.
  • String.format / Formatter - удобен для простого форматирования чисел с учетом локали (через Locale), но не предоставляет автоматической подстановки символа валюты; символ добавляется вручную.
  • JSR 354 (javax.money) - спецификация и реализация для представления денежных величин и операций над ними. Предпочтительна для бизнес-логики и вычислений с валютами, так как обеспечивает типобезопасность и семантику денег.

Выбор зависит от задач: для отображения и простого разбора подойдут NumberFormat и DecimalFormat. Для финансовых вычислений лучше применять библиотеки, реализующие денежную модель (JSR 354 или собственные классы на основе BigDecimal).

Аналоги в других языках

JavaScript

new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(12345.67)
"$12,345.67"

Python

# с библиотекой Babel
from babel.numbers import format_currency
print(format_currency(12345.67, 'USD', locale='en_US'))
"$12,345.67"

PHP

// с расширением intl
$fmt = new NumberFormatter('en_US', NumberFormatter::CURRENCY);
echo $fmt->formatCurrency(12345.67, 'USD');
"$12,345.67"

C#

using System.Globalization;

decimal v = 12345.67m;
Console.WriteLine(v.ToString("C", CultureInfo.GetCultureInfo("en-US")));
"$12,345.67"

Go

import "golang.org/x/text/message"

p := message.NewPrinter(message.Language("en"))
p.Printf("%s", "$12,345.67") // стандартной кросс-локаль поддержки валют нет
"$12,345.67"

SQL (PostgreSQL)

SELECT to_char(12345.67, 'LFM9,999,990.00');
"$12,345.67" (при установленной локали и валютном символе)

Отличие от Java: многие языки предлагают встроенный интернационализированный форматер (JS, PHP intl, Babel в Python), тогда как Java использует NumberFormat и дополнительную спецификацию JSR 354 для бизнес-логики с валютами. В Java при необходимости тонкой настройки часто требуется приведение к DecimalFormat.

Типичные проблемы и ошибки

  • NullPointerException: вызов getCurrencyInstance(null) может привести к NPE. Проверка аргумента Locale решает проблему.
  • ClassCastException: предположение, что возвращаемый NumberFormat всегда является DecimalFormat. Без проверки instanceof приведение может завершиться ошибкой на некоторых реализациях.
  • Потокобезопасность: использование одного экземпляра в нескольких потоках без синхронизации приводит к некорректным результатам.
  • Неожиданное округление: некоторые валюты (например, JPY) имеют 0 дробных знаков. Подстановка другой валюты может изменить число дробных знаков и привести к округлению.
  • Проблемы при разборе строк: наличие пробелов, неразрывных пробелов или локально-позиционированных символов валюты может мешать parse. В таких случаях предварительная нормализация строки облегчит разбор.

Пример ошибки приведения

NumberFormat nf = NumberFormat.getCurrencyInstance(new Locale("ar","SA"));
DecimalFormat df = (DecimalFormat) nf; // может выбросить ClassCastException в нестандартной реализации
ClassCastException: java.text.SomeOtherFormat cannot be cast to java.text.DecimalFormat

Изменения и заметки по версиям

Сам API NumberFormat.getCurrencyInstance в языке Java остается стабилен на протяжении многих версий. В современных релизах JVM изменений в контракте этого метода не было. Основные изменения в экосистеме относятся к появлению и развитию JSR 354 (Money and Currency API), предлагающего специализированные типы для денег и форматирования с расширенными возможностями. При миграции на новые версии платформы следует обратить внимание на обновления локализационных данных в JDK (символы валют, правила группировки), которые влияют на вывод.

Расширенные и редкие примеры

1) Принудительная настройка символа валюты

Пример java
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;

NumberFormat nf = NumberFormat.getCurrencyInstance(Locale.GERMANY);
if (nf instanceof DecimalFormat) {
    DecimalFormat df = (DecimalFormat) nf;
    DecimalFormatSymbols sym = df.getDecimalFormatSymbols();
    sym.setCurrencySymbol("EUR*");
    df.setDecimalFormatSymbols(sym);
    System.out.println(df.format(1234.5));
}
1.234,50 EUR*

2) Форматирование с выключенной группировкой

Пример java
NumberFormat nf = NumberFormat.getCurrencyInstance(Locale.US);
nf.setGroupingUsed(false);
System.out.println(nf.format(1234567.89));
$1234567.89

3) Задание другой валюты и влияние на количество дробных знаков

Пример java
NumberFormat nf = NumberFormat.getCurrencyInstance(Locale.US);
// по умолчанию USD -> 2 дробных знака
nf.setCurrency(java.util.Currency.getInstance("JPY"));
System.out.println(nf.format(1234.56)); // JPY имеет 0 дробных знаков
¥1,235

4) Парсинг в BigDecimal (при наличии DecimalFormat#setParseBigDecimal)

Пример java
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Locale;

NumberFormat nf = NumberFormat.getCurrencyInstance(Locale.US);
if (nf instanceof DecimalFormat) {
    DecimalFormat df = (DecimalFormat) nf;
    df.setParseBigDecimal(true); // доступно в DecimalFormat
    try {
        Object parsed = df.parse("$1234.56");
        if (parsed instanceof BigDecimal) {
            BigDecimal bd = (BigDecimal) parsed;
            System.out.println(bd);
        } else {
            System.out.println(parsed);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
1234.56

5) Комбинация с JSR 354 (пример концептуальный)

Пример java
// Концептуальный пример: использовать javax.money для точной работы с валютой,
// а NumberFormat для отображения при необходимости.
// Money money = Monetary.getDefaultAmountFactory().setCurrency("USD").setNumber( new BigDecimal("1234.56") ).create();
// Для форматирования можно получить строку через MonetaryAmountFormat из реализации JSR 354.
(Зависит от реализации JSR 354, вывод: "$1,234.56")

В продвинутых сценариях рекомендуется проверять тип возвращаемого формата, явно настраивать символы и поведение округления и применять специализированные денежные библиотеки для логики расчетов.

джава NumberFormat.getCurrencyInstance function comments

En
NumberFormat.getCurrencyInstance Returns a currency format for the current default locale