java - 从语义上比较不同类型的数字

java - comparing numbers of different type, semantically

假设我有三个号码:

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

我想通过Comparable界面比较这些数字。但是,这会导致 ClassCastException.

n1.compareTo(n2);

有没有办法(实用函数、外部库、...)来比较不同类型的数字?在上面的示例中,数字 n2 小于 n1,并且 n1 小于 n3,在语义上。

在代码中,我不知道数字的具体类型。因此,我依赖 Comparable 接口。

你不能那样比较它们。 Comparable 是一个通用接口,它只接受相同的类型。

您可以使用一种转换方法:

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

System.out.println(n1.compareTo(n2));

...

public Double convertNumber(Object o) {
    if(o instanceof Double) {
        return (Double)o;
    } else if(o instanceof Long) {
        return ((Long)o).doubleValue();
    } else if(o instanceof Integer) {
        return ((Integer)o).doubleValue();
    }
    return null;
}

完全正确地解释了为什么会出现 ClassCastException - Comparable 接口是通用的,只能用于相同具体类型的对象。

可能还有其他方法可以解决您的问题:

基元及其对象包装器

如果您确定所有数字都是基元 (int, long, float, double) 或代表基元的 classes 的对象 (Integer, Long, Float, Double),那么您可以使用普通比较运营商,例如相当于 n1.compareTo(n2);

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

class 属于 Number

如果您确定所有变量都是扩展 Number 的 Class,即 AtomicInteger, AtomicLong, BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, Short,您可以使用以下内容:

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

编辑

响应与 OP 的评论讨论 - 我们注意到,如果比较两个 BigDecimal,使用 Double 作为 Number 比较的基础 class 可能会产生虚假的平等或 BigInteger 超出 Double 范围的数字(因此 .doubleValue() returns Double.POSITIVE_INFINITYDouble.NEGATIVE_INFINITY 视情况而定)

如果这是一个问题,compareNumbers() 可以更改如下:

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

        return n1c.compareTo(n2c);
    }

/编辑

实施 Comparable 但不扩展 Number

如果您甚至不能确定您的变量是否扩展了 Number,并且您可能正在处理一个带有 class 表示数字的变量,实现 Comparable,但不扩展 Number,我相信您只能比较相同 class 的参数。在这种情况下,这应该可行,但我真的无法想象用例:

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

正如人们提到的,您无法比较它们 "as-is",但您可以实现一个新的比较器,它将接收不同的可能值并知道如何处理它们。下面是一个可能的实现,其中你有不同的构造函数(每个可能的值 - 可能可以改进那部分)将给定值转换为 "base" 类型(在我的例子中是双精度) - 并且 compareTo 方法可以处理所有不同构造函数支持的类型。

</p> <pre><code>public class MyTestClass { public static void main (String[] args) throws java.lang.Exception { MyComparator n1 = new MyComparator(new Integer(432)); MyComparator n2 = new MyComparator(new Long(40)); MyComparator n3 = new MyComparator(new Double(500.12)); } static class MyComparator implements Comparable { double baseVal = 0.0d; public double getValue() { return baseVal; } public MyComparator(double d) { this.baseVal = d; } public MyComparator(Long l) { this.baseVal = l.doubleValue(); } public MyComparator(int i) { this.baseVal = (double)i; } public int compareTo(MyComparator toCompare) { return ((Double)this.getValue()).compareTo(toCompare.getValue()); } public int compareTo(Object o) { // implemented only to support required interface methods return 0; } } }