我是不是太聪明了?
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
是一个非常大的BigDecimal
或BigInteger
,也不能表示为double
的情况。因此你也需要做instanceof
检查。
警告:当value
略大于Long.MAX_VALUE
(或略小于Long.MIN_VALUE
)时,使用doubleValue()
的测试不起作用。原因是由于浮点数的性质,从整数或十进制数到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开始就起作用了……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
定义。它们是为基本类型int
、long
、float
、double
以及它们的包装类型Integer
、Long
、Float
和Double
定义的。您需要继承接口comparable,并在实现如何比较类型之后使用方法compareTo
。
当您使用扩展Number类的类型参数T时,则T表示扩展Number类的类。请注意,比较运算符只能用于原语,而不能用于类(除非它被包装到相应的类中,比如int被包装成Integer)。由于编译器无法确定您是否使用包装类(Long,Integer,Double,Float等)或扩展Number的用户定义类,它会给出编译时错误。例子:-
class MyNumber extends Number{
}
MyNumber类的对象不理解>操作符。请使用比较器来比较对象