为什么java.lang.Number没有实现Comparable?

有谁知道为什么java.lang.Number不实现Comparable ? 这意味着你不能对NumberCollections.sort进行sorting,这在我看来有点奇怪。

讨论后更新:

感谢所有有用的回应。 我最终做了一些关于这个话题的研究 。

为什么java.lang.Number没有实现Comparable的最简单的解释根源于可变性问题。

对于一些评论, java.lang.NumberAtomicIntegerAtomicLongBigDecimalBigIntegerByteDoubleFloatIntegerLongShort的抽象超types。 在那个列表中, AtomicIntegerAtomicLong没有实现Comparable

四处挖掘,我发现在可变types上实现Comparable并不是一个好习惯,因为在比较的过程中或之后,对象可能会发生变化,从而导致比较结果无用。 AtomicLongAtomicInteger都是可变的。 APIdevise人员有预期没有Number实现Comparable因为它会限制未来子types的实现。 的确, AtomicLongAtomicInteger是在java.lang.Number最初实现之后的Java 1.5版本中添加的。

除了可变性之外,这里也可能有其他的考虑。 NumbercompareTo实现必须将所有的数值提升为BigDecimal因为它能够容纳所有的Number子types。 这种提升在math和表演方面的含义对我而言有点不清楚,但是我的直觉发现了这个解决scheme。

值得一提的是下面的expression式:

 new Long(10).equals(new Integer(10)) 

总是false ,这往往会在某个点或另一个地方使每个人都出现。 所以你不但不能比较任意的Number而且你甚至不能确定它们是否相等。

另外,对于真实的原始types( floatdouble ),确定两个值是否相等是棘手的,必须在可接受的误差范围内完成。 尝试代码如下:

 double d1 = 1.0d; double d2 = 0.0d; for (int i=0; i<10; i++) { d2 += 0.1d; } System.out.println(d2 - d1); 

你会留下一些小的差异。

所以回到Number Comparable的问题。 你将如何实现它? 使用类似doubleValue()不会可靠地做到这一点。 记住Number子types是:

  • Byte ;
  • Short
  • Integer ;
  • Long
  • AtomicInteger ;
  • AtomicLong ;
  • Float ;
  • Double
  • BigInteger ; 和
  • BigDecimal

你能编写一个可靠的compareTo()方法吗?它不会分解成一系列if instanceof语句? Number实例只有六种可用的方法:

  • byteValue() ;
  • shortValue() ;
  • intValue() ;
  • longValue() ;
  • floatValue() ; 和
  • doubleValue()

所以我猜Sun会做出(合理的)决定,即Number只能Comparable自己的实例相Comparable

有关答案,请参阅Java bugparade 错误4414323 。 你也可以从comp.lang.java.programmer中find一个讨论

引用Sun对2001年错误报告的回应:

所有的“数字”是不可比较的; 可比较假定可能的数字总数sorting。 这不是浮点数; NaN(不是数字)既不小于,也不大于,也不等于任何浮点值,甚至本身。 {Float,Double} .compare强加的总sorting与浮点“<”和“=”运算符的sorting不同。 此外,按照当前的实现,Number的子类只能与同一类的其他实例相比较。 还有其他的情况,如复数,虽然可以定义一个标准的总sorting, 简而言之,Number的子类是否具有可比性应作为该子类的决定。

为了实现可比数字,你必须为每个子类对编写代码。 它更容易,而不是让子类来实现可比较的。

很可能是因为比较数字的效率会比较低 – 每个数字可以适用的唯一表示forms就是BigDecimal。

相反,Number的非primefaces子类实现了Comparable本身。

primefaces是可变的,所以不能实现primefaces比较。

要尝试解决原始问题(对数字列表进行sorting),一个选项是声明扩展数字的genericstypes的列表并实现Comparable。

就像是:

 <N extends Number & Comparable<N>> void processNumbers(List<N> numbers) { System.out.println("Unsorted: " + numbers); Collections.sort(numbers); System.out.println(" Sorted: " + numbers); // ... } void processIntegers() { processNumbers(Arrays.asList(7, 2, 5)); } void processDoubles() { processNumbers(Arrays.asList(7.1, 2.4, 5.2)); } 

对于不同types的数字没有标准的比较。 然而你可以编写你自己的比较器,并用它来创build一个TreeMap <Number,Object>,TreeSet <Number>或Collections.sort(List <Number>,Comparator)或Arrays.sort(Number [],Comparator);

你可以使用Transmorph来使用它的NumberComparator类比较数字。

 NumberComparator numberComparator = new NumberComparator(); assertTrue(numberComparator.compare(12, 24) < 0); assertTrue(numberComparator.compare((byte) 12, (long) 24) < 0); assertTrue(numberComparator.compare((byte) 12, 24.0) < 0); assertTrue(numberComparator.compare(25.0, 24.0) > 0); assertTrue(numberComparator.compare((double) 25.0, (float) 24.0) > 0); assertTrue(numberComparator.compare(new BigDecimal(25.0), (float) 24.0) > 0); 

写你自己的比较器

 import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; public class NumberComparator implements Comparator { @SuppressWarnings("unchecked") @Override public int compare(Number number1, Number number2) { if (((Object) number2).getClass().equals(((Object) number1).getClass())) { // both numbers are instances of the same type! if (number1 instanceof Comparable) { // and they implement the Comparable interface return ((Comparable) number1).compareTo(number2); } } // for all different Number types, let's check there double values if (number1.doubleValue() < number2.doubleValue()) return -1; if (number1.doubleValue() > number2.doubleValue()) return 1; return 0; } /** * DEMO: How to compare apples and oranges. */ public static void main(String[] args) { ArrayList listToSort = new ArrayList(); listToSort.add(new Long(10)); listToSort.add(new Integer(1)); listToSort.add(new Short((short) 14)); listToSort.add(new Byte((byte) 10)); listToSort.add(new Long(9)); listToSort.add(new AtomicLong(2)); listToSort.add(new Double(9.5)); listToSort.add(new Double(9.0)); listToSort.add(new Double(8.5)); listToSort.add(new AtomicInteger(2)); listToSort.add(new Long(11)); listToSort.add(new Float(9)); listToSort.add(new BigDecimal(3)); listToSort.add(new BigInteger("12")); listToSort.add(new Long(8)); System.out.println("unsorted: " + listToSort); Collections.sort(listToSort, new NumberComparator()); System.out.println("sorted: " + listToSort); System.out.print("Classes: "); for (Number number : listToSort) { System.out.print(number.getClass().getSimpleName() + ", "); } } } 

为什么这会是个坏主意? :

 abstract class ImmutableNumber extends Number implements Comparable { // do NOT implement compareTo method; allowed because class is abstract } class Integer extends ImmutableNumber { // implement compareTo here } class Long extends ImmutableNumber { // implement compareTo here } 

另一种select可能是声明类Number实现了Comparable,省略了CompareTo实现,并在像Integer这样的类中实现它,同时在其他像AtomicInteger中抛出UnsupportedException。

我的猜测是,通过不实现Comparable,它给予更多的灵活性来实现类的实现与否。 所有的常用数字(Integer,Long,Double等)都实现了Comparable。 只要元素本身实现了Comparable,你仍然可以调用Collections.sort。

查看类的层次结构。 像Long,Integer等包装类实现Comparable,即一个Integer可以和一个整数相比,而一个long可以和long相比,但是你不能混用它们。 至less在这个generics范例中。 我猜想你的问题是“为什么”。

byte (primitive)是一个int (原语)。 基元一次只有一个值。
语言devise规则允许这样做。

 int i = 255 // down cast primitive (byte) i == -1 

一个Byte不是一个IntegerByte是一个NumberInteger是一个NumberNumber对象可以同时具有多个值。

 Integer iObject = new Integer(255); System.out.println(iObject.intValue()); // 255 System.out.println(iObject.byteValue()); // -1 

如果一个Byte是一个IntegerInteger是一个Number ,那么你将在compareTo(Number number1, Number number2)方法中使用哪一个值?

Interesting Posts