实现接口比较器

spr*_*ter 7 java comparison

说我有一个简单的interface,我希望Comparable基于一些功能:

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

    @Override
    default int compareTo(Organism other) {
        return this.getComplexity() - other.getComplexity();
    }
}
Run Code Online (Sandbox Code Playgroud)

每个实现类必须返回唯一的复杂性,因此类的任何两个实例将具有相同的复杂性,并且不同类的任何两个实例将具有不同的复杂性.自然排序将所有类的实例"组合"在一起.

我现在想要在一个类中实现此接口,该类重写默认比较,专门用于比较该类的实例组中该类的两个实例.我使用以下模式:

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

    @Override
    public int compareTo(Organism other) {
        if (other instanceof Bacteria)
            return this.shape.compareTo((Bacteria)other.shape);
        else
            return Organism.super.compareTo(other);
    }
}
Run Code Online (Sandbox Code Playgroud)

我对这种代码模式并不是特别满意:一旦实现接口的类集变得很大,维护变得非常复杂并且需要大量重复的代码并依赖于"复杂性"的隐含属性.我更喜欢Comparator定义订单的风格.我想是能够实现Comparable在Bacteria使用的东西,看起来像:

return Comparator
    .comparingInt(Organism::getComplexity)
    .thenComparing(Bacteria::getShape);
Run Code Online (Sandbox Code Playgroud)

为了清楚起见,我意识到比较器不能像这样工作:它们被设计成在一个集合中使用一个比较器,而不是根据每个对象使用不同的比较器.我在这里提到它们并不是因为它们是一种潜在的解决方案,而是因为比较器的链式风格优雅而透明.我感兴趣的是,是否有一种类似的优雅方式来定义,compareTo以允许集合中的不同排序取决于类.

ajb*_*ajb 0

我不确定你打算把Comparator. 既然您希望您的课程实现Comparable<Organism>,我假设您正在寻找类似的东西

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是行不通的,因为thenComparing在上下文中,需要一个参数,该参数是Function对 a 进行操作Organism,而不是对 a进行操作Bacteria。你可以像这样解决它——我将让你来决定这是否足够优雅:

Comparator<Organism> comparator = 
    Comparator
        .comparingInt(Organism::getComplexity)
        .thenComparing(x -> ((x instanceof Bacteria) ? ((Bacteria)x).getShape() : Shape.ROD));
Run Code Online (Sandbox Code Playgroud)

理论上,您也可以编写自己的方法来将比较器转换为另一个比较器。你不能使用instance.method符号,所以使用必须是这样的:

Comparator<Organism> comparator = 
   MyComparatorUtilities.thenComparingIfInstanceOf(
        Comparator.comparingInt(Organism::getComplexity),
        Bacteria.class,
        Bacteria::getShape);
Run Code Online (Sandbox Code Playgroud)

以下thenComparingIfInstanceOf编译实现(并允许上面的代码编译),但我还没有尝试测试它:

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;
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

更多:回答评论:不,我不一定认为这种方法更具可读性或可维护性。事实上,我认为整个设计是不可维护的,因为添加一个类太容易导致比较违反全序的属性;我正在寻找不同的设计,首先是更清晰地定义我希望顺序如何作用于不同类的对象。处理比较的“正确”方法可能取决于不同的设计。

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

  • 您认为这种方法是否比在原始代码中在子类中使用“instanceof”表达式更容易维护和/或更好的可读性? (2认同)