Java泛型:比较整数和双精度浮点数

4

有人能帮我使用泛型比较一个整数和一个双精度浮点数吗?

这是我的代码:

public static <T extends Comparable<? super T>> int compare(T arg1, T arg2)
{
    return arg1.compareTo(arg2);
}

public static void main(String[] args)
{   
    Number i = new Integer(5);
    Number j = new Double(7);

    System.out.println(GenericsTest.compare(i, j));
}

我收到的错误信息是: 边界不匹配:类型GenericsTest的通用方法compare(T,T)不适用于参数(Number,Number)。 推断出的类型Number不是有限制的参数>的有效替代品。

1
Number没有扩展Comparable?这可能是异常的原因吗? - Fildor
但是我想比较具有相同超类型的不同类型。我该怎么做? - karakays
那么你不能使用 Number。它不符合你对超类型的要求。如果你坚持使用你的方法,你需要让超类型扩展 Comparable - Fildor
1
一个小提示,最好使用例如 Integer.valueOf(5) 而不是 new Integer(5),这样可以避免不必要的内存分配。请参阅《Effective Java》。 - SkyWalker
5个回答

6

这个解决方案的想法是将数字扩展到 BigDecimal,然后进行比较(现在更清洁了,但某种程度上格式化不起作用)。请注意,您可以重用此静态比较器,而无需在其他任何地方转换为双精度。在实现中,您确实需要转换为双精度以避免丢失信息,基本上是将数字扩展到最通用的表示形式。

private static final Comparator<Number> NUMBER_COMPARATOR = new Comparator<Number>() {
    private BigDecimal createBigDecimal(Number value) {
        BigDecimal result = null;
        if (value instanceof Short) {
            result = BigDecimal.valueOf(value.shortValue());
        } else 
        if (value instanceof Long) {
            result = BigDecimal.valueOf(value.longValue());             
        } else 
        if (value instanceof Float) {
            result = BigDecimal.valueOf(value.floatValue());                                
        } else 
        if (value instanceof Double) {
            result = BigDecimal.valueOf(value.doubleValue());                               
        } else 
        if (value instanceof Integer) {
            result = BigDecimal.valueOf(value.intValue());                              
        } else {
            throw new IllegalArgumentException("unsupported Number subtype: " + value.getClass().getName());
        }
                       assert(result != null);

        return result;
    }

    public int compare(Number o1, Number o2) {
        return createBigDecimal(o1).compareTo(createBigDecimal(o2));
    };
};

public static void main(String[] args) {
    Number i = Integer.valueOf(5);
    Number j = Double.valueOf(7);
              // -1
    System.out.println(NUMBER_COMPARATOR.compare(i, j));

         i = Long.MAX_VALUE;
         j = Long.valueOf(7);
              // +1
         System.out.println(NUMBER_COMPARATOR.compare(i, j));

         i = Long.MAX_VALUE;
         j = Long.valueOf(-7);
              // +1
         System.out.println(NUMBER_COMPARATOR.compare(i, j));

         i = Long.MAX_VALUE;
         j = Double.MAX_VALUE;
              // -1
         System.out.println(NUMBER_COMPARATOR.compare(i, j));

    i = Long.MAX_VALUE;
    j = Long.valueOf(Long.MAX_VALUE - 1);
    // +1
    System.out.println(NUMBER_COMPARATOR.compare(i, j));

              // sorting Long values
    Long[] values = new Long[] {Long.valueOf(10), Long.valueOf(-1), Long.valueOf(4)};
    Arrays.sort(values, NUMBER_COMPARATOR);
              // [-1, 4, 10] 
    System.out.println(Arrays.toString(values));  
}

1
longdouble 都是 64 位类型。它们代表不同范围的数字。你不能无损地将 long 映射到 double。我相信 Long.MAX_VALUELong.MAX_VALUE-1 将映射到相同的 double,因此你的 Comparator 将无法对它们进行排序。即使你比较 longValuedoubleValue,也无法涵盖其他数字,例如 BigInteger 和任何第三方类型。 - Tom Hawtin - tackline
是的,我确实将impl扩展到了最高范围的BigDecimal,并且还包括了您建议的边界情况测试。现在是不是无懈可击了? - SkyWalker

1

正如评论中所说,Number没有实现Comparable接口。 但是DoubleInteger实现了。

使其工作的一种方法是这样的:

public static <T extends Comparable<? super T>> int compare(T arg1, T arg2)
{
    return arg1.compareTo(arg2);
}

public static void main(String[] args)
{   
    Double i = new Integer(5).doubleValue();
    Double j = new Double(7);

    System.out.println(GenericsTest.compare(i, j));
}

我认为我的解决方案更好,因为你不需要每次想要比较时都进行转换,而是只需一次完成并封装在比较器本身中。 - SkyWalker

1

Number doesn't implement Comparable.

将两个变量声明为整数。


0
private boolean compareObject(Object expected, Object actual) {
    if (expected instanceof Number && actual instanceof Number) {
        double e = ((Number) expected).doubleValue();
        double a = ((Number) actual).doubleValue();
        return e == a;
    } else {
        return com.google.common.base.Objects.equal(expected, actual);
    }
}

-1
创建一个实现Comparable接口的类,该类在构造函数中需要传入一个Number。 例如:
public class GenericNumber implements Comparable<GenericNumber> {
    private Number num;
    public GenericNumber(Number num) {
        this.num = num;
    }
    // write compare function that compares num member of two
    // GenericNumber instances
}

然后只需要这样做:

GenericNumber i = new GenericNumber(new Integer(5));
GenericNumber j = new GenericNumber(new Double(7));
System.out.println(GenericsTest.compare(i,j));

  1. 不需要额外的“GenericNumber”,因为超类型“Number”已经存在了。
  2. 你没有定义解决方案的核心,也就是如何通用地进行比较。
  3. 当你需要比较现有类型时,应该创建一个自定义的“Comparator<T>”,而不是添加一个新类型“Comparable”。当你已经有一个类型,并且它只有一种(或默认的)比较方式时,将其设置为可比较更加合适。
- SkyWalker
但是超类型Number没有实现Comparable接口,这就是为什么你需要创建一个包装器的原因。 - RokL
不需要包装器。这正是为什么有Comparator<T>接口的原因,因此您可以向现有类型添加自定义比较器,并且它与Java API的其余部分(例如Collections、Arrays等)完美集成。 - SkyWalker
真的,但是OP想要创建一个不使用比较器的静态函数。 - RokL
确实如此,但是楼主可能不知道所有的可能性。除此之外,您可以使用静态访问NUMBER_COMPARATOR的相同函数,并保留OP签名,真的不需要一个GenericNumber,因为这就是Number的作用。 - SkyWalker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接