Java 实现接口比较器

Java 实现接口比较器,java,comparison,Java,Comparison,假设我有一个简单的界面,我希望基于某些功能将其与进行比较: interface Organism extends Comparable<Organism> { String getName(); int getComplexity(); @Override default int compareTo(Organism other) { return this.getComplexity() - other.getComplexity(

假设我有一个简单的
界面
,我希望基于某些功能将其与
进行比较

interface Organism extends Comparable<Organism> {
    String getName();
    int getComplexity();

    @Override
    default int compareTo(Organism other) {
        return this.getComplexity() - other.getComplexity();
    }
}
我对这种代码模式并不特别满意:一旦实现接口的类集变得很大,维护起来就变得相当复杂,需要大量重复的代码,并且依赖于“复杂性”的隐式属性。我更喜欢定义顺序的
Comparator
风格。我希望能够在
Bacteria
中实现
Comparable
,使用如下内容:

return Comparator
    .comparingInt(Organism::getComplexity)
    .thenComparing(Bacteria::getShape);

明确地说,我意识到比较器不是这样工作的:它们的设计是为了在一个集合中使用一个比较器,而不是根据每个对象使用不同的比较器。我在这里提到它们不是因为它们是一个潜在的解决方案,而是因为比较器的链接风格优雅而透明。我感兴趣的是,是否有一种类似的优雅方式来定义
比较对象
,以允许在一个集合中根据类别进行不同的排序。

我不确定您计划将
比较对象
放在哪里。由于您希望您的类实现
Comparable
,因此我假设您正在寻找类似

class Bacteria implements Organism {
    enum Shape {ROD, ROUND, SPIRAL};
    private final Shape shape;

    Comparator<Organism> comparator = 
        Comparator
            .comparingInt(Organism::getComplexity)
            .thenComparing(Bacteria::shape);  // illegal

    @Override
    public int compareTo(Organism other) {
        return comparator.compare(this, other);
    }
}
理论上,您也可以编写自己的方法,将一个比较器转换为另一个比较器。您不能使用
instance.method
表示法,因此必须这样使用:

Comparator<Organism> comparator = 
   MyComparatorUtilities.thenComparingIfInstanceOf(
        Comparator.comparingInt(Organism::getComplexity),
        Bacteria.class,
        Bacteria::getShape);
更多:回答评论:不,我不一定认为这种方法更具可读性或可维护性。事实上,我相信整个设计是不可维护的,因为添加一个类太容易了,它会导致比较违反总排序的属性;我会寻找一种不同的设计,从更清晰的定义开始,我希望排序如何在不同类的对象上工作。处理比较的“正确”方式可能取决于不同的设计


对于类似的问题,我可能会坚持使用
compareTo
方法,除非出于其他原因(例如,
organic
s定义了多个排序),我需要一个类来返回
Comparator
)。但是,如果每个
compareTo
都是具有相同结构的
if
语句,或者类似的语句,我可能会寻找消除重复的方法

您可以执行以下操作:

return Comparator
      .comparingInt(Organism::getComplexity)
      .thenComparing(o-> o instanceof Bacteria ? 
             ((Bacteria) o).getShape() : Bacteria.Shape.ROD);
如果是细菌,它会比较形状,否则它会比较相同类型的常数,它们总是相等的。如果在比较中不使用相同的类型,它将无法编译


这不是一个通用接口,但这可能会帮助其他试图比较有限数量子类属性的人。

对我来说,这听起来像一个
比较器。
。注意:您的比较类型可能不是可传递的!假设
a
是复杂度为1的细菌,形状为2,
b
是复杂度为2的非细菌生物体,
c
是复杂度为3的细菌,形状为1。注意,
a
b
c
。我同意@immibis。我认为,因为您没有定义有效的比较,所以这个问题没有意义。@immibis注意,我包含了一条注释,即每个类的实例相对于默认比较是相等的。所有细菌都与所有树对象等具有相同的复杂性。因此我明确排除了您的场景。
一旦实现接口的类集变大,维护就变得相当复杂了
-为什么您认为维护会变得复杂?您是否预见到,
细菌
比较方法需要根据扩展
生物体
的其他类别进行更改?或者您认为维护起来会很复杂,因为整个层次结构的比较逻辑分散在子类中?您认为这种方法比在子类中像在原始代码中那样有一个
instanceof
表达式更容易维护和/或可读性更好吗?@ajb感谢您的回答。在设计的问题上,我不一定不同意你的观点——我不喜欢这样一个事实,即它只通过在复杂性上合作的实现来工作。尽管我对如何优雅地实现这一点感兴趣:如何允许接口的实现定义自己的排序机制。我不确定我是否在任何标准代码中看到过一个不增加很多不必要复杂性的好例子。
Comparator<Organism> comparator = 
   MyComparatorUtilities.thenComparingIfInstanceOf(
        Comparator.comparingInt(Organism::getComplexity),
        Bacteria.class,
        Bacteria::getShape);
class MyComparatorUtilities {
    public static 
    <T,U extends T,V extends Comparable<? super V>> Comparator<T> thenComparingIfInstanceOf(
        Comparator<T> comparator,
        Class<U> subclass,
        Function<? super U, ? extends V> keyExtractor) {
        return (a, b) -> {
            int comp = comparator.compare(a, b);
            if (comp != 0) {
                return comp;
            }
            if (subclass.isInstance(a) && subclass.isInstance(b)) {
                return keyExtractor.apply(subclass.cast(a))
                    .compareTo(keyExtractor.apply(subclass.cast(b)));
            }
            return 0;
        };
    }
}
return Comparator
      .comparingInt(Organism::getComplexity)
      .thenComparing(o-> o instanceof Bacteria ? 
             ((Bacteria) o).getShape() : Bacteria.Shape.ROD);