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 Number
s 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
; andBigDecimal
.
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()
; anddoubleValue()
.
So I guess Sun made the (reasonable) decision that Number
s 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 Number
s 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
; andBigDecimal
.
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()
; anddoubleValue()
.
So I guess Sun made the (reasonable) decision that Number
s 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 equalNaN
, which is extremely hairy/impossible to compare since all comparisons withNaN
result infalse
, 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
How to Use 3Rd Party Library in Java9 Module
How to Pass an Integer Class Correctly by Reference
How to Get Subnet Mask of Local System Using Java
Service Layer and Controller: Who Takes Care of What
How to Use Collections.Sort() in Java
Java: Get Month Integer from Date
@Preupdate and @Prepersist in Hibernate/JPA (Using Session)
In Java, How to Call a Base Class's Method from the Overriding Method in a Derived Class
How to Add Days to a Date in Java
The Easiest Way to Transform Collection to Array
How to Intercept a Method Invocation with Standard Java Features (No Aspectj etc)
Junit - Run Set Up Method Once
Native Query with Named Parameter Fails with "Not All Named Parameters Have Been Set"
Circular Dependency in Java Constructors