Assume you have some objects which have several fields they can be compared by:
public class Person {
private String firstName;
private String lastN
Writing a Comparator
manually for such an use case is a terrible solution IMO. Such ad hoc approaches have many drawbacks:
So what's the solution?
First some theory.
Let us denote the proposition "type A
supports comparison" by Ord A
. (From program perspective, you can think of Ord A
as an object containing logic for comparing two A
s. Yes, just like Comparator
.)
Now, if Ord A
and Ord B
, then their composite (A, B)
should also support comparison. i.e. Ord (A, B)
. If Ord A
, Ord B
, and Ord C
, then Ord (A, B, C)
.
We can extend this argument to arbitrary arity, and say:
Ord A, Ord B, Ord C, ..., Ord Z
⇒ Ord (A, B, C, .., Z)
Let's call this statement 1.
The comparison of the composites will work just as you described in your question: the first comparison will be tried first, then the next one, then the next, and so on.
That's the first part of our solution. Now the second part.
If you know that Ord A
, and know how to transform B
to A
(call that transformation function f
), then you can also have Ord B
. How? Well, when the two B
instances are to be compared, you first transform them to A
using f
and then apply Ord A
.
Here, we are mapping the transformation B → A
to Ord A → Ord B
. This is known as contravariant mapping (or comap
for short).
Ord A, (B → A)
⇒comap Ord B
Let's call this statement 2.
Now let's apply this to your example.
You have a data type named Person
that comprises three fields of type String
.
We know that Ord String
. By statement 1, Ord (String, String, String)
.
We can easily write a function from Person
to (String, String, String)
. (Just return the three fields.) Since we know Ord (String, String, String)
and Person → (String, String, String)
, by statement 2, we can use comap
to get Ord Person
.
QED.
How do I implement all these concepts?
The good news is you don't have to. There already exists a library which implements all the ideas described in this post. (If you are curious how these are implemented, you can look under the hood.)
This is how the code will look with it:
Ord personOrd =
p3Ord(stringOrd, stringOrd, stringOrd).comap(
new F>() {
public P3 f(Person x) {
return p(x.getFirstName(), x.getLastname(), x.getAge());
}
}
);
Explanation:
Ord
. This corresponds to our original "supports comparison" proposition.Ord
, Ord
, Ord
, and returns Ord>
. This corresponds to statement 1. (P3 stands for product with three elements. Product is an algebraic term for composites.)comap
.A → B
.Hope that helps.