Search code examples
javanumberscomparisoncomparatorcomparable

java - comparing numbers of different type, semantically


Say I have three numbers:

Comparable n1 = new Integer(432);
Comparable n2 = new Long(40);
Comparable n3 = new Double(500.12);

I would like to compare these numbers through the Comparable interface. However, this results in a ClassCastException.

n1.compareTo(n2);

Is there a way (utility function, external library, ...) to compare numbers of different type? In the example above, the number n2 is smaller than n1, and n1 is smaller than n3, semantically.

In the code, I have no knowledge about the concrete type of the numbers. Hence, I rely on the Comparable interface.


Solution

  • The first answer is exactly right on why you get the ClassCastException - the Comparable interface is generic and can only be used on objects of the same concrete type.

    There may be other ways to solve your problem:

    Primitives and their object wrappers

    If you are sure that all your numbers are primitives (int, long, float, double) or Objects for the classes that represent primitives (Integer, Long, Float, Double), then you can just use normal comparison operators e.g. for equivalence to n1.compareTo(n2);

    int b = n1 > n2 ? 1 : (n1 < n2 ? -1 : 0);
    

    Subclasses of Number

    If you are sure that all your variables are of a Class that extends Number i.e. AtomicInteger, AtomicLong, BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, Short, you can use the following:

        private int compareNumbers(Number n1, Number n2)
        {
            Double n2c = n2.doubleValue();
            Double n1c = n1.doubleValue();
    
            return n1c.compareTo(n2c);
        }
    
        public static void main (String[] args)
        {
            Integer n1 = new Integer(432);
            Long n2 = new Long(40);
            Double n3 = new Double(500.12);
    
            System.out.println(compareNumbers(n1,n2));
        }
    

    EDIT

    In response to comment discussion with OP - we noted that using Double as the base class for the Number comparison might give spurious equality if comparing two BigDecimal or BigInteger numbers that are beyond the range of Double (therefore .doubleValue() returns Double.POSITIVE_INFINITY or Double.NEGATIVE_INFINITY as appropriate)

    If this is a concern, compareNumbers() could be altered as follows:

        private int compareNumbers(Number n1, Number n2)
        {
            BigDecimal n2c = new BigDecimal(n2.toString());
            BigDecimal n1c = new BigDecimal(n1.toString());
    
            return n1c.compareTo(n2c);
        }
    

    /EDIT

    Implement Comparable but don't extend Number

    If you can't even be sure that your variables extend Number, and you may be processing a variable with a class that represents a number, implements Comparable, but does not extend Number, I believe you can only compare the arguments if they are of the same class. In this case, this should work, but I can't really imagine the use case:

        Comparable n1 = new WierdNonNumberExtendingInteger(432);
        Comparable n2 = new WierdNonNumberExtendingLong(40);
        Comparable n3 = new WierdNonNumberExtendingDouble(500.12);
    
        Integer b =  null;
        if (n1.getClass().isInstance(n2))
        {
           b =  n1.compareTo(n2);
        }
        else
        {
           System.err.println("Can't compare apples and oranges");
           System.err.print("You tried to compare " + n1.getClass().getSimpleName());
           System.err.print(" with " + n2.getClass().getSimpleName());
        }
    
        System.out.println(b);