RomanNumeralHelper

整数与罗马数字互转

基本信息

属性 值
路径 squeek.tictooltips.helpers.RomanNumeralHelper
行数 61
源码注释 改编自两个 StackOverflow 答案(:5)

两张字典(static 初始化)

字符 → 值(:9-17):

I V X L C D M
1 5 10 50 100 500 1000

值 → 罗马串(:19-32),TreeMap(键升序,13 项):

键 值 键 值 键 值 键 值
1000 M 900 CM 500 D 400 CD
100 C 50 L 40 XL 10 X
9 IX 5 V 4 IV 1 I

含全部 6 个减法写法(CM CD XL IX IV),标准写法。

toRoman(:34-40)—— 递归

int key = NumberRomanDictionary.floorKey(number);
if (number == key) return NumberRomanDictionary.get(key);
return NumberRomanDictionary.get(key) + toRoman(number - key);

用 floorKey 取不超过 number 的最大键,然后递归处理余数。

⚠️ number < 1 时抛异常:TreeMap.floorKey(0) / floorKey(-5) 返回 null, NumberRomanDictionary.get(null) 抛 NullPointerException。

调用方 StringHelper 的 getReinforcedString 用 if (reinforced > 9) return ...unbreakable 把 >= 10 挡掉了, 所以实际只会传 0-9。但 toRoman(0) 也会抛 —— floorKey(0) 在这张表里返回 null(最小键是 1), 所以强化等级 0 的工具会崩。 不过 getReinforcedString 只在 mat.reinforced() > 0 时被调用 (TooltipHandler.java:278),实际路径上 0 不会进来。

fromRoman(:42-59)—— 迭代

⚠️ 逆转换用了一个非常规写法:不是标准的「小值在大值前则减」, 而是 subtract 整个差值再回加:

if (previous != 0 && current > previous) total = total - (2 * previous) + current;
else total += current;

total - 2*previous + current 等价于「减去 previous 的两次再补 current」, 在标准减法记法下是正确的(IX:+1 然后 -2*1+10 = 9)。

两处实际缺陷

  1. :49 的 previous 初值是字符 '\0' 而非 0:

    previous = previousRoman != '\0' ? RomanNumberDictionary.get(previousRoman) : '\0';
    

    三元表达式的 else 分支给 int previous 赋了字符字面量 '\0'(值为 0)。 数值上恰好等于想要的 0,但类型不匹配(char 隐式转 int),属于会误导读者的写法。

  2. 未知字符抛 NPE —— RomanNumberDictionary.get(currentRoman) 对 不在字典里的字符(如小写 i、空格)返回 null, total += current 处自动拆箱 null → NullPointerException。 fromRoman 没有大小写归一化,传小写罗马数字必崩。

fromRoman 无调用点

grep 全仓,fromRoman 在本 mod 内没有任何调用, 只有 toRoman 被 StringHelper 使用。 它是为对称性保留的未使用 API —— 且 apiPackage 在 gradle.properties 中为空,不对外暴露。

相关

  • StringHelper - 唯一实际调用方(getReinforcedString)