Why Doesn't Java.Lang.Number Implement Comparable

Why doesn't java.lang.Number implement Comparable?

It's worth mentioning that the following expression:

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

is always false, which tends to trip everyone up at some point or another. So not only can you not compare arbitrary Numbers but you can't even determine if they're equal or not.

Also, with the real primitive types (float, double), determining if two values are equal is tricky and has to be done within an acceptable margin of error. Try code like:

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

and you'll be left with some small difference.

So back to the issue of making Number Comparable. How would you implement it? Using something like doubleValue() wouldn't do it reliably. Remember the Number subtypes are:

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

Could you code a reliable compareTo() method that doesn't devolve into a series of if instanceof statements? Number instances only have six methods available to them:

  • byteValue();
  • shortValue();
  • intValue();
  • longValue();
  • floatValue(); and
  • doubleValue().

So I guess Sun made the (reasonable) decision that Numbers were only Comparable to instances of themselves.

Why doesn't Number use Comparable/Comparator

When a class implements comparable there's only one compareTo() method. You can't define two or more compareTo() methods in one class. What if you want to compare some other values ? Then you can implement new class with comparator interface to use it with other values. Comparator interface comes to help in such case.

Number is an abstract class - there's nothing to compare there. This class is extended by concrete classess like Integer, Double etc - this classess have concrete values to compare.

Here's a great example how to use both interfaces:
http://javarevisited.blogspot.com/2011/06/comparator-and-comparable-in-java.html

Why doesn't java.lang.Number implement Comparable?

It's worth mentioning that the following expression:

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

is always false, which tends to trip everyone up at some point or another. So not only can you not compare arbitrary Numbers but you can't even determine if they're equal or not.

Also, with the real primitive types (float, double), determining if two values are equal is tricky and has to be done within an acceptable margin of error. Try code like:

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

and you'll be left with some small difference.

So back to the issue of making Number Comparable. How would you implement it? Using something like doubleValue() wouldn't do it reliably. Remember the Number subtypes are:

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

Could you code a reliable compareTo() method that doesn't devolve into a series of if instanceof statements? Number instances only have six methods available to them:

  • byteValue();
  • shortValue();
  • intValue();
  • longValue();
  • floatValue(); and
  • doubleValue().

So I guess Sun made the (reasonable) decision that Numbers were only Comparable to instances of themselves.

cannot cast to java.lang.Comparable

It's because Pair isn't implementing Comparable. Either implement it:

public class Pair implements Comparable<Pair> {
public int compareTo(Pair o) {
// ...
}
}

Or use Comparator in Your priority queue

Using Comparator ;

PriorityQueue<DummyObject> pq = new
PriorityQueue<DummyObject>(5, new DummyObjectComparator());

Define your Comparator :

class DummyObjectComparator implements Comparator<DummyObject>{

// Overriding compare()method of Comparator

public int compare(DummyObject s1, DummyObject s2) {
//some code
}
}

Why the Number class doesn't have methods like add() or negate()?

Most importantly, to preserve backward compatibility.

These methods that you mention weren't in the first version of java.lang.Number, and they would need to be abstract.
Why abstract? Suppose you created your own ComplexNumber subclass of Number before it had a negate() method, and now a negate() method is added to Number.
If the negate() method wasn't abstract, there should be a general implementation of it in java.lang.Number. What type of object should it return? There is no possible good decision for this. Should it be a Double, like this: public Number negate() { return new Double(this.doubleValue()); }? That would suddenly convert your ComplexNumber to a Double on a negate.

And adding abstract methods wouldn't work because it would break existing third-party subclasses of Number, so it would break backward compatibility.

Backward compatibility is the reason that a lot of possible API improvements that seem obvious now cannot be implemented.

I don't understand why my Comparable interface isn't working

java.lang.Object, which is your superclass, does not implement Comparable. You should change the signature of your class to:

public abstract class Employee implements Cloneable, Comparable<Employee>

And then implement this method:

public int compareTo(Employee other) {
// logic to compare here
}

With actual logic to do the comparison (aka, don't try and depend on some other objects implementation).

Java: Overloading (generic) Methods for java.lang.Number

[ Sorry, I didn't fully understand the question with my first try. ]

I don't think there is an easy answer here @Rafael because as @Andrei Bodnarescu pointed out, type erasure means that you do not have the type of your N parameter at runtime. I think you have to provide a concrete implementation of your add() method for each subclass of Number.

public RealNumber<Integer> add(Integer number) {
return new RealNumber<Integer>(intValue() + number);
}
public RealNumber<Long> add(Long number) {
return new RealNumber<Long>(longValue() + number);
}

If you don't want to add integers to doubles then I guess you will need to do something like:

public RealNumber<Integer> add(Integer number){
if (!(this instanceof Integer)) {
throw new IllegalArgumentException("You can't do this...");
}
return new RealNumber<Integer>(intValue() + number);
}

I don't see any easy way to work around this.

Comparing the values of two generic Numbers

A working (but brittle) solution is something like this:

class NumberComparator implements Comparator<Number> {

public int compare(Number a, Number b){
return new BigDecimal(a.toString()).compareTo(new BigDecimal(b.toString()));
}

}

It's still not great, though, since it counts on toString returning a value parsable by BigDecimal (which the standard Java Number classes do, but which the Number contract doesn't demand).

Edit, seven years later: As pointed out in the comments, there are (at least?) three special cases toString can produce that you need to take into regard:

  • Infinity, which is greater than everything, except itself to which it is equal
  • -Infinity, which is less than everything, except itself to which it is equal
  • NaN, which is extremely hairy/impossible to compare since all comparisons with NaN result in false, including checking equality with itself.

Why should a Java class implement comparable?

Here is a real life sample. Note that String also implements Comparable.

class Author implements Comparable<Author>{
String firstName;
String lastName;

@Override
public int compareTo(Author other){
// compareTo should return < 0 if this is supposed to be
// less than other, > 0 if this is supposed to be greater than
// other and 0 if they are supposed to be equal
int last = this.lastName.compareTo(other.lastName);
return last == 0 ? this.firstName.compareTo(other.firstName) : last;
}
}

later..

/**
* List the authors. Sort them by name so it will look good.
*/
public List<Author> listAuthors(){
List<Author> authors = readAuthorsFromFileOrSomething();
Collections.sort(authors);
return authors;
}

/**
* List unique authors. Sort them by name so it will look good.
*/
public SortedSet<Author> listUniqueAuthors(){
List<Author> authors = readAuthorsFromFileOrSomething();
return new TreeSet<Author>(authors);
}

How to make a generic lambda that compares any Number of identical type

The first thing you have to understand that you are getting no benefit from writing Operator<Integer>. Java uses auto-boxing to convert int to the Integer wrapper object. Then in your code ((v1, v2) -> v1 >= v2) the Integer object is converted back into an int.

A more universal solution would use the Comparable interface. Number doesn't implement the interface, but all the important Number implementations like Double support it.

Operator<Comparable<?>> gt = (v1, v2) -> v1.compareTo(v2) > 0;
Operator<Comparable<?>> ge = (v1, v2) -> v1.compareTo(v2) >= 0;
Operator<Comparable<?>> ge = (v1, v2) -> v1.compareTo(v2) = 0;
Operator<Comparable<?>> le = (v1, v2) -> v1.compareTo(v2) <= 0;
Operator<Comparable<?>> lt = (v1, v2) -> v1.compareTo(v2) < 0;

compareTo returns 1 if the parameter is smaller, 0 if it's the same size and -1 of it is bigger.



Related Topics



Leave a reply



Submit