对于参数Number,int,运算符<未定义

Ric*_*ard 6 java generics

我想在这里过于聪明吗?

  private static <T extends Number> Long extractLong(T value) {
    if ( value < Long.MIN_VALUE || value > Long.MAX_VALUE ) {   // <= compile error
      throw new NumberFormatException("Conversion from " + value + " to Long will overflow");
    }
    return value.longValue();
  }
Run Code Online (Sandbox Code Playgroud)

这会产生编译错误:

对于参数类型T,long,运算符>未定义

但是,如果我明确地执行该函数,它将编译:

  private static Long extractLong(Long value) {
    if ( value < Long.MIN_VALUE || value > Long.MAX_VALUE ) {
      throw new NumberFormatException("Conversion from " + value + " to Long will overflow");
    }
    return value.longValue();
  } 
Run Code Online (Sandbox Code Playgroud)

isn*_*bad 5

由于Java不支持操作符重载,这是不可能的定义比较操作符的含义<>任何类型的对象。换句话说:

Number a = new Long(1);
Number b = new Long(2);
if (a < b) // does NOT compile - objects cannot be compared using `<` or `>`!
Run Code Online (Sandbox Code Playgroud)

第二个示例进行编译的原因是自动装箱。该Long对象被自动转换为long你做这当然是相当的值。但是由于没有针对类型的对象的通用自动装箱Number,因此在第一种情况下不起作用。

那么我们如何检查溢出?我认为最简单的方法是先检查该double值:

private static long extractLong(Number value) {
    double v = value.doubleValue();
    if (v < Long.MIN_VALUE || v > Long.MAX_VALUE) {
        throw new NumberFormatException(...);
    }
    return value.longValue();
}
Run Code Online (Sandbox Code Playgroud)

请注意,这并不涵盖其中value很大BigDecimalBigInteger不能表示为double任一情况的情况。因此,您也需要进行instanceof检查。

警告:此测试使用的值大于或小于doubleValue()时不起作用。原因是由于浮点值的性质,从整数或十进制数到n的转换不准确。更具体地说,所有从到的值都将转换为一个相同的双值()。例:valueLong.MAX_VALUELong.MIN_VALUEdoubleLong.MAX_VALUE - 511Long.MAX_VALUE + 10259.223372036854776E18

double d1 = Long.MAX_VALUE - 511;
double d2 = Long.MAX_VALUE;
double d3 = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.valueOf(1025)).doubleValue();
// d1 == d2 == d3 due to lack of precision of double!
Run Code Online (Sandbox Code Playgroud)

但是,这意味着,上面的表达式v > Long.MAX_VALUE将是false对所有这些值虽然它们中的一些有效大于Long.MAX_VALUE

long l = extractLong(BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE));
// l is now -9223372036854775808 => overflow check failed!
Run Code Online (Sandbox Code Playgroud)

旁注:此处不必使用泛型!只需Number直接使用即可T,您可以传递各种数字。由于多态性,这从Java 1.0开始就可以使用...;)