为什么java.lang.Number没有实现Comparable?
有谁知道为什么java.lang.Number
不实现Comparable
? 这意味着你不能对Number
和Collections.sort
进行sorting,这在我看来有点奇怪。
讨论后更新:
感谢所有有用的回应。 我最终做了一些关于这个话题的研究 。
为什么java.lang.Number没有实现Comparable的最简单的解释根源于可变性问题。
对于一些评论, java.lang.Number
是AtomicInteger
, AtomicLong
, BigDecimal
, BigInteger
, Byte
, Double
, Float
, Integer
, Long
和Short
的抽象超types。 在那个列表中, AtomicInteger
和AtomicLong
没有实现Comparable
。
四处挖掘,我发现在可变types上实现Comparable
并不是一个好习惯,因为在比较的过程中或之后,对象可能会发生变化,从而导致比较结果无用。 AtomicLong
和AtomicInteger
都是可变的。 APIdevise人员有预期没有Number
实现Comparable
因为它会限制未来子types的实现。 的确, AtomicLong
和AtomicInteger
是在java.lang.Number
最初实现之后的Java 1.5版本中添加的。
除了可变性之外,这里也可能有其他的考虑。 Number
的compareTo
实现必须将所有的数值提升为BigDecimal
因为它能够容纳所有的Number
子types。 这种提升在math和表演方面的含义对我而言有点不清楚,但是我的直觉发现了这个解决scheme。
值得一提的是下面的expression式:
new Long(10).equals(new Integer(10))
总是false
,这往往会在某个点或另一个地方使每个人都出现。 所以你不但不能比较任意的Number
而且你甚至不能确定它们是否相等。
另外,对于真实的原始types( float
, double
),确定两个值是否相等是棘手的,必须在可接受的误差范围内完成。 尝试代码如下:
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
不是一个Integer
。 Byte
是一个Number
, Integer
是一个Number
。 Number
对象可以同时具有多个值。
Integer iObject = new Integer(255); System.out.println(iObject.intValue()); // 255 System.out.println(iObject.byteValue()); // -1
如果一个Byte
是一个Integer
而Integer
是一个Number
,那么你将在compareTo(Number number1, Number number2)
方法中使用哪一个值?