无法在网络上找到合适的解决方案,因此我想问一下我使用java格式的方式是否正确。
1)在NumberFormat.java文档中,它表示
数字格式通常不同步。建议为每个线程创建单独的格式实例。
我们一直在多线程环境中使用格式对象(静态初始化),到目前为止没有任何问题。是否因为一旦定义了格式,我们的状态就不会改变(即之后不再调用setter)
2)我现在需要定义一个新格式,它应该在逗号后输出一个或两个有效数字,具体取决于一些额外的逻辑。我这样做的方法是定义一个新的格式包装器,并根据覆盖的#format(double,StringBuffer,FieldPosition)方法中的情况委托给两个不同的DecimalFormat。这是代码:
private final NumberFormat FORMAT = new DecimalFormat() {
private final NumberFormat DECIMAL_FORMAT = new DecimalFormat("0.##");
private final NumberFormat DECIMAL_FORMAT_DIGIT = new DecimalFormat(
"0.0#");
public StringBuffer format(double number, StringBuffer result, java.text.FieldPosition fieldPosition) {
if ((number >= 10 && Math.ceil(number) == number)) {
return DECIMAL_FORMAT.format(number, result, fieldPosition);
} else {
return DECIMAL_FORMAT_DIGIT.format(number, result, fieldPosition);
}
}
};
这是最佳做法吗?我担心没有实际使用包装器类(它只用于遵守NumberFormat接口并委托内部格式的所有工作)。我不想调用DecimalFormat#applyPattern(),因为我认为这会破坏易失性并发性。
由于
答案 0 :(得分:3)
- 我们一直在多线程环境中使用格式对象(静态初始化),到目前为止没有任何问题。可能是因为一旦定义了格式,我们的状态就不会改变(即之后不会调用setter)
醇>
不可能确切地说出为什么没有看到任何问题,因为我们并不确切知道你是如何使用它们的。在我的头脑中,有几个原因可能是:
DecimalFormat
中的任何代码路径; 正如我昨天看到其他人评论的那样,关于同步问题的一点是如果不同步,你不能保证看到问题;只是你不能保证不会看到它们。
关键在于,如果你没有应用同步,那么你可能会完全不知道任何数量的细微变化。今天它有效,明天它没有;你将有一个全能的工作来解决原因。
- 这是最佳做法吗?
醇>
我可以在这里想到几个问题:
通过扩展课程,您可能会犯fragile base class problem。
简而言之,除非您实际上明确地在public StringBuffer format(double, StringBuffer, java.text.FieldPosition )
实例上调用DecimalFormat
方法,否则您无法可靠地知道被覆盖的方法是否实际上是被调用的方法:对实现的更改基类(DecimalFormat
)可能会改变您依赖的逻辑来调用该方法。
您有三个可变实例 - FORMAT
,DECIMAL_FORMAT
和DECIMAL_FORMAT_DIGIT
- 它们具有各种设置器来改变其行为。
你应将所有这些设置者传播到所有实例,以便它们的行为一致,例如如果您在setPositivePrefix
上致电FORMAT
,则还应在DECIMAL_FORMAT
和DECIMAL_FORMAT_DIGIT
上调用相同的方法。
除非您确实需要将FORMAT
作为参数传递给方法,否则如果您只定义了一个调用逻辑的普通旧方法,它会更加健壮:基本上,移动您要覆盖的方法匿名子类:
private static StringBuffer formatWithSpecialLogic(double number, StringBuffer result, java.text.FieldPosition fieldPosition) {
如果您想使用该特殊逻辑,那么必须显式调用该方法。
答案 1 :(得分:1)
我没有评论的声誉,但就第2点而言,我看到的最大缺点是你没有覆盖DecimalFormat类的所有行为,因此你的结果实例将表现不一致。例如:
final StringBuffer buffer = new StringBuffer();
FORMAT.format(0.111d, buffer, new FieldPosition(0));
System.out.println(buffer.toString());
final StringBuffer buffer2 = new StringBuffer();
FORMAT.format(new BigDecimal(0.111d), buffer2, new FieldPosition(0));
System.out.println(buffer2.toString());
产量
0.11
0.111
如果你走这条路线,那么覆盖所有必要的方法会更好,这样你就能获得一致的行为。或者,您可以在ThreadLocal中存储一个封装所需逻辑的函数:
private final ThreadLocal<Function<Double, String>> DECIMAL_FORMATTER = new ThreadLocal<Function<Double, String>>() {
@Override
protected Function<Double, String> initialValue() {
final DecimalFormat decimalFormat = new DecimalFormat("0.##");
final DecimalFormat decimalFormatDigit = new DecimalFormat("0.0#");
return (number) -> {
if ((number >= 10 && Math.ceil(number) == number)) {
return decimalFormat.format(number);
} else {
return decimalFormatDigit.format(number);
}
};
}
};
System.out.println(DECIMAL_FORMATTER.get().apply(0.111d));
答案 2 :(得分:1)
既然你说你需要有一个NumberFormat
的实例,我建议你扩展那个类并实现所需的方法,而不是扩展那些不用于继承的DecimalFormat
:
private final NumberFormat FORMAT = new NumberFormat() {
private final NumberFormat DECIMAL_FORMAT = new DecimalFormat("0.##");
private final NumberFormat DECIMAL_FORMAT_DIGIT = new DecimalFormat("0.0#");
@Override
public StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition) {
if ((number >= 10 && Math.ceil(number) == number)) { // or number % 1 == 0
return DECIMAL_FORMAT.format(number, result, fieldPosition);
} else {
return DECIMAL_FORMAT_DIGIT.format(number, result, fieldPosition);
}
}
@Override
public StringBuffer format(long number, StringBuffer result, FieldPosition fieldPosition) {
return format((double)number, result, fieldPosition);
}
@Override
public Number parse(String source, ParsePosition parsePosition) {
return DECIMAL_FORMAT.parse(source, parsePosition);
}
};
这减少了脆弱的基类问题,因为我们正在使用一个意味着要继承的基类。但是仍然存在所有死配置方法的问题。理想情况下,您可以覆盖它们以传播其更改或抛出一些异常。