In the entry about object schizophrenia I used java.lang.Comparable
as an example in J#, but for IKVM I did not mention it as a ghost interface. In the comments Stuart Ballard asked why Comparable isn't a ghost interface.
Unlike what Stuart claimed, this is definitely not a stupid question. He also figured out part of the answer: java.lang.Comparable
maps to System.IComparable
.
That's not the whole story though. The reason that I originally mapped Comparable
to IComparable
was that System.String
conveniently implements IComparable
and that IComparable.CompareTo
is semantically (nearly) identical to Comparable.compareTo
. Back then I hadn't yet "invented" the ghost interface idea and this seemed to solve most of the issues (the other two ghost interface candidates, Cloneable
and Serializable
are only used as marker interfaces, so it didn't seem like a big deal that System.String
didn't implement these interfaces. java.lang.CharSequence
only appeared in Java 1.4 and I vaguely knew about it, but at the time I chose not to worry about that yet.)
A nice side effect of mapping Comparable
to IComparable
is that you get better interop (e.g. Java objects that implement Comparable
will sort correctly when used in .NET applications and .NET objects that implement IComparable
will sort correctly in Java), so that meant that when I developed ghost interfaces I didn't really want to go back and change Comparable
into a ghost interface.
Finally, there is one more complication. Even though Comparable.compareTo
and IComparable.CompareTo
have identical semantics, there still is a problem with String
. Java's String.compareTo
is specified more strictly than Comparable.compareTo
. The interface method is specified to simply return zero or a signed or unsigned integer, but the String
version is actually specified to return the difference between the mismatching characters (or zero, if the strings match.) To handle this correctly, when you call Comparable.compareTo
, you're actually calling a static method Comparable.__Helper.compareTo
that first does a check to see if the object you're comparing is a String
and, if so, it calls a static helper method that implements the specified Java comparision algorithm.
BTW, the reason that the static compareTo helper method lives inside a nested __Helper class is because Reflection.Emit (on .NET 1.x) incorrectly prohibits adding static methods to interfaces (which is entirely legal according to the CLI specification).
Performance Impact
The following table compares the direct methods calls with the interface method calls. Note that these basically represent a worst case estimate, because of limit amount of work the actual compareTo method does. Note also that for notational convenience I used the zero digit, but in the actual benchmark I used a local variable containing new Integer(0)
.
Method Call |
Time (ns) |
"".compareTo((Object)"") |
140 |
((Comparable)"").compareTo("") |
220 |
0.compareTo(0) |
6 |
((Comparable)0).compareTo(0) |
35 |
These results are on .NET Framework 2.0 (x86). Averaged over 10,000,000 calls and includes the loop overhead.
For comparison, here are the results for JDK 1.5 HotSpot Client VM:
Method Call |
Time (ns) |
"".compareTo((Object)"") |
17 |
((Comparable)"").compareTo("") |
21 |
0.compareTo(0) |
7 |
((Comparable)0).compareTo(0) |
15 |