Richard
Richard

Reputation: 3100

Operator < is undefined for argument Number, int

Am I trying to be too clever here?

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

which yields the compile error:

The operator > is undefined for the argument type(s) T, long

But if I do the function explicitly it compiles :

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

Upvotes: 6

Views: 7017

Answers (5)

isnot2bad
isnot2bad

Reputation: 24454

As Java does not support operator overloading, it is not possible to define the meaning of the comparison operators < and > for any type of objects. Or in other words:

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

The reason why your second example compiles is autoboxing. The Long objects are automatically converted to long values which are of course comparable as you did. But as there is no generic autoboxing for objects of type Number, this does not work in the first case.

So how can we check for overflow then? The simplest way I think is to check the double value first:

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

Note that this does not cover the case where value is a very large BigDecimal or BigInteger that cannot be expressed as double either. Therefore you'd need to do instanceof checks too.

Warning: This test using doubleValue() does not work when value is slightly larger than Long.MAX_VALUE (or slightly smaller than Long.MIN_VALUE). The reason is that due to the nature of floating point values, conversion from integral or decimal numbers to double is not exact. More concrete, all values from Long.MAX_VALUE - 511 to Long.MAX_VALUE + 1025 will be converted to one and the same double value (9.223372036854776E18). Example:

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!

But this means, that the above expression v > Long.MAX_VALUE will be false for all those values although some of them are effectively larger than Long.MAX_VALUE:

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

Side note: It is not necessary to use generics here! Just use Number directly instead of T and you can pass all kinds of numbers. Thanks to polymorphism, this works since Java 1.0... ;)

Upvotes: 5

Syam S
Syam S

Reputation: 8499

Basically the T here could be treated as an implementation of abstract class java.lang.Number. And Number does not support any of the arithemetic or logic operator like +, -, <, >, <= etc. as Java does not support operator overloading. However the out-of-box implementation class like Integer, Long, Double etc would work with these operators as they are being auto-boxed to int, long, double etc. In your case when you say the type as T the compiler cannot determine what implementation it would use until runtime. It can be an implementation which doesn't support auto-boxing. So compiler shown error.

But when you change the argument type to Long the compiler know for sure that this could be auto-boxed an the operator could be applied. So no error. As a solution if you could use like

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

Upvotes: 3

Kumar Abhinav
Kumar Abhinav

Reputation: 6675

When you use a type argument T which extends Number class,then T represents a class which extends Number class.Please note that comparison operators can only be used for primitives,not for classes (unless it is wrapped to its corresponding class,say int is wrapped to Integer).Since,the compiler cannot ascertain whether you are using a wrapper class(Long,Integer,Double,Float etc) or a user defined class which extends Number ,it gives compile time error. Example:-

class MyNumber extends Number{
}

Objects of MyNumber class do not understand > operator.Please use a comparator for comparing Objects.

Upvotes: 1

Simulant
Simulant

Reputation: 20122

> < >= <= are not defined for your Type T. They are defined for the primitve types int, long, float double and as a special for their wrapper types Integer, Long, Float and Double. you need to inherrit from interface comparable and use the method compareTo after you implemented how to compare your Types.

Upvotes: 1

nni6
nni6

Reputation: 1000

Only numeric types can be compared using the ordering operators in Java. T can be not numeric, so compiler doesn't allow this. Use Comparator to compare objects.

Upvotes: 2

Related Questions