在我看来,为这样的用例手动编写Comparator是一个糟糕的解决方案。这种特别的方法有很多缺点:
没有代码重用。违反了干。
样板。
增加了出错的可能性。
那么解决方案是什么呢?
首先是一些理论。
让我们用Ord A来表示命题“type A支持比较”(从程序的角度来看,您可以将Ord A看作一个包含比较两个A的逻辑的对象。是的,就像Comparator一样。)
现在,如果Ord A和Ord B,那么它们的合成(A, B)也应该支持比较。例如,Ord (A, B)。如果Ord A, Ord B,和Ord C,那么Ord (A, B, C)。
我们可以将这个论证扩展到任意性,并说:
A站,B站,C站,……, Ord Z⇒Ord (A, B, C, .., Z)
我们称这个为表述一。
复合材料的比较将像您在问题中描述的那样工作:首先尝试第一个比较,然后是下一个比较,然后是下一个比较,依此类推。
这是解的第一部分。现在是第二部分。
如果你知道A字词,也知道如何将B转换为A(称之为转换函数f),那么你也可以有B字词,怎么做?当要比较两个B实例时,首先使用f将它们转换为A,然后应用Ord A。
在这里,我们将变换B→A映射到Ord A→Ord B,这被称为逆变映射(或简称comap)。
Ord A, (B→A)
我们称这个为表述二。
现在让我们把这个应用到你的例子中。
您有一个名为Person的数据类型,它包含三个String类型的字段。
我们知道Ord String。通过语句1,Ord(字符串,字符串,字符串)。
我们可以很容易地编写一个从Person到(String, String, String)的函数。(只返回三个字段。)由于我们知道Ord (String, String, String)和Person→(String, String, String),通过语句2,我们可以使用comap来获得Ord Person。
QED.
我如何实现所有这些概念?
好消息是你不必这么做。已经有一个库实现了这篇文章中描述的所有想法。(如果你对这些是如何实现的感到好奇,你可以深入了解一下。)
下面是代码的外观:
Ord<Person> personOrd =
p3Ord(stringOrd, stringOrd, stringOrd).comap(
new F<Person, P3<String, String, String>>() {
public P3<String, String, String> f(Person x) {
return p(x.getFirstName(), x.getLastname(), x.getAge());
}
}
);
解释:
stringOrd是Ord<String>类型的对象。这与我们最初的“支持比较”命题相对应。
p3Ord是一个获取Ord< a >, Ord<B>, Ord<C>,并返回Ord<P3< a, B, C>>的方法。这对应于表述一。(P3代表三要素产品。Product是复合材料的代数术语。)
Comap对应于Comap。
F<A, B>表示变换函数A→B。
P是创建产品的工厂方法。
整个表达式对应于表述2。
希望这能有所帮助。