使用 DecimalFormat 格式化会抛出异常 - "Cannot format given Object as a Number"

Formatting using DecimalFormat throws exception - "Cannot format given Object as a Number"

这可能看起来像一个重复的问题,但我尝试了以下所有链接,但无法得到正确的答案。

Illegal Argument Exception

但我不明白哪里出了问题。这是我的代码

DecimalFormat twoDForm = new DecimalFormat("#.##");
double externalmark = 1.86;
double internalmark = 4.0;
System.out.println(String.valueOf((externalmark*3+internalmark*1)/4));
String val = String.valueOf((externalmark*3+internalmark*1)/4);
String wgpa1=twoDForm.format(val); // gives exception
String wgpa2=twoDForm.format((externalmark*3+internalmark*1)/4)); // works fine
System.out.println(wgpa1);

format 方法接受 Object 类型参数,所以这就是为什么我传递了一个 String 对象,它给出了异常

Exception in thread "main" java.lang.IllegalArgumentException: Cannot format given Object as a Number.

但是当我给出双精度值作为参数时程序工作正常。但是,如果该方法是用 Object 类型参数定义的,为什么我在传递 String 时出现异常,而在传递 double 时没有出现异常?

答案就在javadoc中。它说得很清楚,"The number can be of any subclass of Number",它说它抛出 IllegalArgumentException "if number is null or not an instance of Number."

(那么他们为什么不直接将参数设为 Number 类型呢?因为 class 是抽象 Format [=24 的子class =] 不限于数字格式。显然,期望是,虽然一般 Format class 有一个带有 Object 参数的方法,但 subclasses Format 的人希望将参数限制为他们可以处理的对象类型,他们必须在 运行 时间完成。)

DecimalFormatformat() 方法被重载。

在工作案例中,您正在调用:

 public final String format(double number)

在失败的情况下,您正在调用:

 public final String format (Object obj) 

第一种方法采用非常具体的参数。它需要 double.

第二个不是这种情况,它接受的类型非常广泛:Object 因此在运行时对传递的类型进行检查。

通过提供一个不是 double 而是 String 的参数,调用的方法是第二个方法。

在幕后,此方法依赖于 format(Object number, StringBuffer toAppendTo, FieldPosition pos) 方法,该方法期望 number 参数是 Number class 的实例(Short, Long, ... Double):

@Override
public final StringBuffer format(Object number,
                                 StringBuffer toAppendTo,
                                 FieldPosition pos) {
    if (number instanceof Long || 
        number instanceof Integer ||                   
        number instanceof Short || 
        number instanceof Byte ||                   
        number instanceof AtomicInteger ||
        number instanceof AtomicLong ||
        (number instanceof BigInteger && ((BigInteger)number).bitLength () < 64)) {

        return format(((Number)number).longValue(), toAppendTo, pos);
    } else if (number instanceof BigDecimal) {
        return format((BigDecimal)number, toAppendTo, pos);
    } else if (number instanceof BigInteger) {
        return format((BigInteger)number, toAppendTo, pos);
    } else if (number instanceof Number) {
        return format(((Number)number).doubleValue(), toAppendTo, pos);
    } else {
        throw new IllegalArgumentException("Cannot format given Object as a Number");
    }
}

但情况并非如此,因为您向它传递了一个 String 实例。

要解决这个问题,要么像成功案例那样传递一个 double 原语,要么将你的 String 转换成 Number 的实例,例如 DoubleDouble.valueOf(yourString).
我建议第一种方式(传递 double),因为它在已经使用 double 原语的代码中更自然。
第二个需要额外的从StringDouble的转换操作。

如果有任何数学计算那么使用java.math.BigDecimalclass的方法是更好的选择结果的准确性和性能效率偶数太大了。 使用 java.math.BigDecimal 代码:

double externalmark1 = 1.86;
double internalmark2 = 4.0;
System.out.println(String.valueOf((externalmark1*3+internalmark2*1)/4));
System.out.println("------------------------");

BigDecimal decimalValue1 = new BigDecimal((externalmark1*3+internalmark2*1)/4).setScale(2, RoundingMode.HALF_UP);       
System.out.println("aggregatemark [direct decimalValue]: "+decimalValue1.toString());
System.out.println("------------------------");

double aggregatemark = (externalmark1*3+internalmark2*1)/4;
System.out.println("aggregatemark [double]: "+aggregatemark);       
BigDecimal decimalValue2 = new BigDecimal(aggregatemark).setScale(2, RoundingMode.HALF_UP);     
System.out.println("aggregatemark [decimalValue]: "+decimalValue2.toString());
System.out.println("------------------------");

String aggregatemarkStr = String.valueOf((externalmark1*3+internalmark2*1)/4);
System.out.println("aggregatemark [string] : "+aggregatemarkStr);       
BigDecimal decimalValue3 = new BigDecimal(aggregatemarkStr).setScale(2, RoundingMode.HALF_UP);      
System.out.println("aggregatemark [decimalValue]: "+decimalValue3.toString());