Details
-
Bug
-
Status: Closed
-
Minor
-
Resolution: Fixed
-
None
-
None
Description
DefaultTypeTransformation.compareTo(Object left, Object right) is being used in plenty of places in Groovy.
However, for several corner cases, it does not provide symmetry, that is: when compareTo(a, b) returns a value (does not fail), then:
signum(compareTo(a, b)) == - signum(compareTo(b, a))
To reproduce:
static class MyNumber extends Number { def n MyNumber(n) { this.n = n } int intValue(){n} long longValue(){n} float floatValue(){n} double doubleValue(){n} int hashCode(){-n} boolean equals(other) { if (other instanceof MyNumber) { return n==other.n} return false } String toString() {n.toString()} } static class MyNumberCompareTo extends MyNumber { MyNumberCompareTo(Object n) { super(n) } int compareTo(MyNumber other) { return n <=> other.n } } static class MyNumberComparable extends MyNumberCompareTo implements Comparable<MyNumber> { MyNumberComparable(Object n) { super(n) } int compareTo(Object other) { return n <=> (MyNumber) other; } } void testCompareTo() { def object1 = new Object() def object2 = new Object() // objects assert compareTo(null, null) == 0 assert compareTo(object1, null) == 1 assert compareTo(null, object1) == -1 assert compareTo(1, 1) == 0 shouldFail(GroovyRuntimeException) { compareTo(object1, object2) } // chars, int values 49 and 50 Character char1 = '1' as Character Character char2 = '2' as Character checkCompareToSymmetricSmallerThan(char1, char2) MyNumber number1 = new MyNumber(49) MyNumber number2 = new MyNumber(50) MyNumberCompareTo numCompTo1 = new MyNumberCompareTo(49) MyNumberCompareTo numCompTo2 = new MyNumberCompareTo(50) MyNumberComparable numComp1 = new MyNumberComparable(49) MyNumberComparable numComp2 = new MyNumberComparable(50) List lowers = [49, 49L, 49.0, 49.0G, 49.00G, char1, '1', number1, numCompTo1, numComp1] List highers = [50, 50L, 50.0, 50.0G, 50.00G, char2, '2', number2, numCompTo2, numComp2] lowers.each { def lower -> assert compareTo(lower, lower) == 0 highers.each { def higher -> checkCompareToSymmetricSmallerThan(lower, higher) } } // glitch, failing with ClassCastException shouldFail(GroovyRuntimeException) { compareTo(1, "22") } shouldFail(GroovyRuntimeException) { compareTo("22", 1) } // strings and chars assert compareTo('aa1', '2' as Character) > 0 // bug, classCast exception assert compareTo('2' as Character, 'aa1') < 0 assert compareTo("aa${1}", '2' as Character) > 0 // bug, classCast exception assert compareTo('2' as Character, "aa${1}") < 0 // Strings and GStrings List lowers2 = ['aa1', "aa${1}"] List highers2 = ['bb2', "b${2}"] lowers2.each { def lower -> assert compareTo(lower, lower) == 0 highers2.each { def higher -> checkCompareToSymmetricSmallerThan(lower, higher) } } } static void checkCompareToSymmetricSmallerThan(a, b) { try { assert compareTo(a, b) < 0 assert compareTo(b, a) > 0 } catch (AssertionError e) { System.err.print(a.class.toString() + ' compared to ' + b.class.toString() ) throw e } }
Attachments
Attachments
Issue Links
- links to