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

时间:2014-07-30 11:15:58

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

产生编译错误:

  

运营商&gt;未定义参数类型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();
  } 

5 个答案:

答案 0 :(得分:5)

由于Java不支持运算符重载,因此无法为任何类型的对象定义比较运算符<>的含义。换句话说:

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

您的第二个示例编译的原因是autoboxingLong个对象会自动转换为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检查。

警告:当doubleValue()略大于value(或稍微小于Long.MAX_VALUE)时,使用Long.MIN_VALUE的此测试无效。原因是由于浮点值的性质,从整数或十进制数转换为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 ...;)

答案 1 :(得分:3)

基本上,这里的T可以被视为抽象类java.lang.Number的实现。并且Number不支持像+, -, <, >, <=等任何arithemetic或逻辑运算符,因为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();
}

答案 2 :(得分:2)

只能使用Java中的排序运算符来比较数字类型。 T可以不是数字,因此编译器不允许这样做。使用Comparator比较对象。

答案 3 :(得分:1)

当你使用扩展Number类的类型参数T时,T代表一个扩展Number类的类。请注意,比较运算符只能用于基元,而不能用于类(除非它被包装到相应的类,说int被包装到Integer)。因为,编译器无法确定您是使用包装类(Long,Integer,Double,Float等)还是扩展Number的用户定义类,它会产生编译时错误。 例如: -

class MyNumber extends Number{
}

MyNumber类的对象不理解&gt; operator.Please使用比较器来比较对象。

答案 4 :(得分:1)

您的类型> < >= <=未定义

T。它们是针对原始类型intlongfloat double定义的,并且是其包装类型IntegerLong,{的特殊类型{1}}和Float。您需要从可比较的接口继承并在实现如何比较类型后使用方法Double