我想在这里过于聪明吗?
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();
}
答案 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 `>`!
您的第二个示例编译的原因是autoboxing。 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
非常大的BigDecimal
或BigInteger
也不能表示为double
的情况。因此,您也需要进行instanceof
检查。
警告:当doubleValue()
略大于value
(或稍微小于Long.MAX_VALUE
)时,使用Long.MIN_VALUE
的此测试无效。原因是由于浮点值的性质,从整数或十进制数转换为double
并不精确。更具体地说,从Long.MAX_VALUE - 511
到Long.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
。它们是针对原始类型int
,long
,float
double
定义的,并且是其包装类型Integer
,Long
,{的特殊类型{1}}和Float
。您需要从可比较的接口继承并在实现如何比较类型后使用方法Double
。