操作符& lt;对于参数Number, int是未定义的



我是不是太聪明了?

  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();
  }

产生编译错误:

对于实参类型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();
  } 

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

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

第二个示例编译的原因是自动装箱。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();
}

注意,这并不包括value是一个非常大的BigDecimalBigInteger,也不能表示为double的情况。因此你也需要做instanceof检查。

警告:value略大于Long.MAX_VALUE(或略小于Long.MIN_VALUE)时,使用doubleValue()的测试不起作用。原因是由于浮点数的性质,从整数或十进制数到double的转换是不精确的。更具体地说,从Long.MAX_VALUE - 511Long.MAX_VALUE + 1025的所有值都将转换为一个相同的双精度值(9.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!

但是这意味着,上面的表达式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!

旁注:这里没有必要使用泛型!只要直接使用Number代替T,就可以传递各种数字。多亏了多态,这从Java 1.0开始就起作用了……div;)

基本上这里的T可以被视为抽象类java.lang.Number的实现。Number不支持任何算术或逻辑运算符,如+, -, <, >, <=等,因为Java不支持运算符重载。然而,开箱即用的实现类,如Integer, Long, Double等,将与这些操作符一起工作,因为它们被自动装箱到int, long, double等。在您的情况下,当您说类型为T时,编译器无法确定它将使用哪种实现,直到运行时。它可以是一个不支持自动装箱的实现。编译器显示错误

但是当你将实参类型更改为Long时,编译器肯定知道这可以自动装箱,并且可以应用操作符。所以没有误差。作为解决方案,如果你可以使用

private static <T extends Number> Long extractLong(T value) {
    if ( value.doubleValue() < Long.MIN_VALUE || value.doubleValue() > Long.MAX_VALUE ) {   
      throw new NumberFormatException("Conversion from " + value + " to Long will overflow");
    }
    return value.longValue();
}

在Java中只有数字类型可以使用排序操作符进行比较。T可以不是数字,所以编译器不允许这样做。使用Comparator来比较对象

> < >= <=没有为您的类型T定义。它们是为基本类型intlongfloatdouble以及它们的包装类型IntegerLongFloatDouble定义的。您需要继承接口comparable,并在实现如何比较类型之后使用方法compareTo

当您使用扩展Number类的类型参数T时,则T表示扩展Number类的类。请注意,比较运算符只能用于原语,而不能用于类(除非它被包装到相应的类中,比如int被包装成Integer)。由于编译器无法确定您是否使用包装类(Long,Integer,Double,Float等)或扩展Number的用户定义类,它会给出编译时错误。例子:-

class MyNumber extends Number{
}

MyNumber类的对象不理解>操作符。请使用比较器来比较对象

最新更新