Neverending Groovy: compareTo() simple as it can be with <=>

Groovy introduced a new operator to compare two values: <=>.

When you use this operator for 2 values a <=> b, Groovy translates this to code something like that a.compareTo(b). Well that's not the true, as it also can handle null values correctly. So you wouldn't get NPE, when you compare value with null or vice versa. For instance, next script:


a = null
b = 12

println "a.compareTo(b) = " + (a <=> b)
println "a.compareTo(a) = " + (a <=> a)
println "b.compareTo(b) = " + (b <=> b)

outputs

a.compareTo(b) = -1
a.compareTo(a) = 0
b.compareTo(b) = 0

where we can see, that null values are handled correctly, without any runtime exceptions.

With such operator we could write really nice and simple code to allow multiple comparisons for object values.

Lets we have a class Person with firstName, lastName and age data:


class Person {
String firstName
String lastName
int age
}


And we need to sort a list of Persons by age, than by name (first by last name, than by first name). Assume, this is standard sorting mode, so we may prefer to implement Comparable interface:



class Person implements Comparable {
String firstName
String lastName
int age

int compareTo(Person other) {
...
}
}


Method compareTo() isn't implemented yet. So lets try to implement in Java first. It would look something like this:


int compareTo(Person other) {
if (other == null) {
return 1;
}

int result = 0;
if (this.age > other.age) {
result = 1;
}
else if (this.age < other.age) {
result = -1;
}
if (result == 0) {
if (this.lastName != null && other.lastName != null) {
result = this.lastName.compareTo(other.lastName);
if (result == 0) {
if (this.firstName != null && other.firstName != null) {
result = this.firstName.compareTo(other.firstName);
}
else {
result = this.firstName != null ? 1 : -1;
}
}
}
else {
result = this.lastName != null ? 1 : -1;
}
}
return result;
}


Hm... Looks ugly! Too much if's, too much checks, too much 0, 1 and -1. Hard to see the logic behind the code ((.

And here how this can be written with Groovy's <=> operator:


int compareTo(Person other) {
age <=> other.age ?: lastName <=> other.lastName ?: firstName <=> other.firstName
}


And a simple test:


def john = new Person(firstName: 'John', lastName: 'Doe', age: 25)
def mark = new Person(firstName: 'Mark', lastName: 'White', age: 25)

print "john <=> mark = " + (john <=> mark)

prints

john <=> mark = -1


As for me, it looks wonderful.

No comments: