1. 概述

本教程将介绍如何在 Java 中将货币金额转换为英文单词表示。我们将通过两种方式实现:

  • ✅ 自定义实现(从零开始构建转换逻辑)
  • ✅ 使用第三方库(Tradukisto)

面向有经验的开发者,基础概念将简明扼要,重点展示核心实现和踩坑点。

2. 自定义实现

2.1 核心数据结构

第一步是定义两个基础字符串数组,用于存储数字单词表示:

public static String[] ones = { 
  "", "one", "two", "three", "four", 
  "five", "six", "seven", "eight", 
  "nine", "ten", "eleven", "twelve", 
  "thirteen", "fourteen", "fifteen", 
  "sixteen", "seventeen", "eighteen", 
  "nineteen" 
};

public static String[] tens = {
  "",          // 0
  "",          // 1
  "twenty",    // 2
  "thirty",    // 3
  "forty",     // 4
  "fifty",     // 5
  "sixy",      // 6
  "seventy",   // 7
  "eighty",    // 8
  "ninety"     // 9
};

2.2 输入处理与拆分

接收到输入后,需处理非法值(零或负数)。有效输入需拆分为美元和美分:

long dollars = (long) money;
long cents = Math.round((money - dollars) * 100);

2.3 分段转换逻辑

根据数字范围采用不同处理策略:

  • 小于20的数字:直接从 ones 数组获取

    if (n < 20) {
        return ones[(int) n];
    }
    
  • 小于100的数字:组合 tensones 数组

    if (n < 100) {
        return tens[(int) n / 10] 
          + ((n % 10 != 0) ? " " : "") 
          + ones[(int) n % 10];
    }
    
  • 更大数字:递归处理千/百万/十亿单位

    if (n < 1_000_000) {
        return convert(n / 1000) + " thousand" + ((n % 1000 != 0) ? " " : "") 
          + convert(n % 1000);
    }
    

2.4 主转换方法

核心转换逻辑整合:

public static String getMoneyIntoWords(double money) {
    long dollars = (long) money;
    long cents = Math.round((money - dollars) * 100);
    if (money == 0D) {
        return "";
    }
    if (money < 0) {
        return INVALID_INPUT_GIVEN;
    }
    String dollarsPart = "";
    if (dollars > 0) {
        dollarsPart = convert(dollars) 
          + " dollar" 
          + (dollars == 1 ? "" : "s");
    }
    String centsPart = "";
    if (cents > 0) {
        if (dollarsPart.length() > 0) {
            centsPart = " and ";
        }
        centsPart += convert(cents) + " cent" + (cents == 1 ? "" : "s");
    }
    return dollarsPart + centsPart;
}

2.5 测试用例验证

常规金额测试

@Test
void whenGivenDollarsAndCents_thenReturnWords() {
    String expectedResult = "nine hundred twenty four dollars and sixty cents";
    assertEquals(expectedResult, NumberWordConverter.getMoneyIntoWords(924.6));
}

@Test
void whenTwoBillionDollarsGiven_thenReturnWords() {
    String expectedResult = "two billion one hundred thirty three million two hundred" 
      + " forty seven thousand eight hundred ten dollars";
    assertEquals(expectedResult, NumberWordConverter.getMoneyIntoWords(2_133_247_810));
}

边界情况测试

@Test
void whenZeroDollarsGiven_thenReturnEmptyString() {
    assertEquals("", NumberWordConverter.getMoneyIntoWords(0));
}

@Test
void whenNoDollarsAndNineFiveNineCents_thenCorrectRounding() {
    assertEquals("ninety six cents", NumberWordConverter.getMoneyIntoWords(0.959));
}

@Test
void whenNoDollarsAndOneCent_thenReturnCentSingular() {
    assertEquals("one cent", NumberWordConverter.getMoneyIntoWords(0.01));
}

⚠️ 踩坑提示

  • 美分四舍五入需用 Math.round() 而非强制转换
  • 单复数处理(dollar/dollars, cent/cents)需严格判断

3. 使用第三方库

3.1 引入 Tradukisto 库

Tradukisto 是专门用于数字转单词的 Java 库。Maven 依赖:

<dependency>
    <groupId>pl.allegro.finance</groupId>
    <artifactId>tradukisto</artifactId>
    <version>1.0.1</version>
</dependency>

3.2 简单粗暴的实现

使用 MoneyConverters 一行搞定转换:

public String getMoneyIntoWords(String input) {
    MoneyConverters converter = MoneyConverters.ENGLISH_BANKING_MONEY_VALUE;
    return converter.asWords(new BigDecimal(input));
}

3.3 库使用测试

@Test
void whenGivenDollarsAndCents_thenReturnWordsVersionTwo() {
    assertEquals("three hundred ten £ 00/100", 
                 NumberWordConverter.getMoneyIntoWords("310"));
}

优势

  • 零维护成本
  • 内置多语言支持

局限

  • 输出格式固定(如 £ 00/100 后缀)
  • 不支持自定义货币单位

4. 总结

两种方案对比:

方案 适用场景 维护成本 定制灵活性
自定义实现 特殊格式需求/无依赖环境 ⭐⭐⭐⭐⭐
Tradukisto 快速实现/标准化输出 ⭐⭐

决策建议

  • 生产环境优先选择库方案(简单可靠)
  • 特殊格式需求(如中文金额)需自定义实现

完整代码示例见 GitHub 项目


原始标题:Displaying Money Amounts in Words