假设我有一个简单的interface
我希望Comparable
基于某些功能:
interface Organism extends Comparable<Organism> {
String getName();
int getComplexity();
@Override
default int compareTo(Organism other) {
return this.getComplexity() - other.getComplexity();
}
}
每个实现类必须返回唯一的复杂性,因此类的任何两个实例将具有相同的复杂性,并且不同类的任何两个实例将具有不同的复杂性。自然顺序将“组合”所有类的实例。
我现在想在一个类中实现这个接口,该类重写默认比较,专门用于比较该类的实例组中该类的两个实例。我使用以下模式:
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);
}
}
我对这种代码模式并不特别满意:一旦实现接口的类集变得很大,维护变得非常复杂并且需要大量重复的代码并依赖于“复杂性”的隐含属性。我更喜欢定义订单的Comparator
样式。我希望能够使用以下内容在Comparable
中实现Bacteria
:
return Comparator
.comparingInt(Organism::getComplexity)
.thenComparing(Bacteria::getShape);
要清楚,我意识到比较器不能像这样工作:它们被设计成在一个集合中使用一个比较器,而不是根据每个对象使用不同的比较器。我在这里提到它们并不是因为它们是一种潜在的解决方案,而是因为比较器的链式风格优雅而透明。我感兴趣的是,是否有一种同样优雅的方式来定义compareTo
以允许集合中的不同排序取决于类。
答案 0 :(得分: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);
}
}
这不起作用,因为thenComparing
在上下文中需要一个Function
参数,该参数对Organism
而不是Bacteria
进行操作。你可以像这样解决它 - 我会留给你判断这是否足够优雅:
Comparator<Organism> comparator =
Comparator
.comparingInt(Organism::getComplexity)
.thenComparing(x -> ((x instanceof Bacteria) ? ((Bacteria)x).getShape() : Shape.ROD));
理论上,您还可以编写自己的方法,将比较器转换为另一个比较器。您不能使用instance.method
表示法,因此使用必须是这样的:
Comparator<Organism> comparator =
MyComparatorUtilities.thenComparingIfInstanceOf(
Comparator.comparingInt(Organism::getComplexity),
Bacteria.class,
Bacteria::getShape);
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;
};
}
}
更多:回答评论:不,我不一定认为这种方法更具可读性或可维护性。事实上,我认为整个设计是不可维护的,因为添加一个会导致比较违反总排序属性的类太容易了;我正在寻找一种不同的设计,从更明确的定义开始,我希望订单如何处理不同类的对象。处理比较的“正确”方式可能取决于不同的设计。
对于类似的问题,我可能坚持使用compareTo
方法,除非我因某些其他原因需要一个类来返回Comparator
(例如,为Organism
s定义了多个排序)。但是,如果每个compareTo
都是具有相同结构的if
语句,或者类似的话,我可能会寻找消除重复的方法。
答案 1 :(得分:0)
您可以执行以下操作:
return Comparator
.comparingInt(Organism::getComplexity)
.thenComparing(o-> o instanceof Bacteria ?
((Bacteria) o).getShape() : Bacteria.Shape.ROD);
因此,如果它是细菌,那么它会比较形状,否则它会比较相同类型的常量相等的常量。如果你在比较中没有使用相同的类型,它就不会编译。
这不是通用接口,但这可能会帮助其他人尝试比较有限数量的子类的属性。