我有一个基本的BusinessObject抽象类,它通过比较它们的长 id字段来实现 Comparable 。现在想象我用Person扩展它,然后我用Worker扩展 Person 。所以我们有:
业务对象 < 人员 < 工人
所以现在我覆盖了 Person(比较名称)和 Worker(比较工作名称,然后是人员名称)中业务对象中的 compareTo(BusinessObject)。
现在我做这样的事情:
List<BusinessObject> collection = new ArrayList<>();
collection.add(new Worker(1L, "Steve", "janitor"));
collection.add(new Worker(2L, "Mark", "plumber"));
collection.add(new Person(3L, "Dave"));
Collections.sort(collection);
System.out.println(collection);
Run Code Online (Sandbox Code Playgroud)
通过记录,我可以看到进行的调用:
所以,这意味着排序方法是混合的,这显然是不好的。那么什么是使用继承实现 Comparable 的正确方法,以便调用的方法取决于集合的泛型类型:
这是我的代码:
public abstract class BusinessObject implements HasId, HasText, Comparable<BusinessObject> {
protected @Nullable Long id;
public BusinessObject(@Nullable Long id) {
this.id = id;
}
@Override
public @Nullable Long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@Override
public int compareTo(@NonNull BusinessObject o) {
System.out.println("compareTo BusinessObject");
return Long.compare(id, o.id);
}
@Override
public String toString() {
return String.format("BusinessObject#%d", id);
}
}
public class Person extends BusinessObject {
protected final String name;
public Person(@Nullable Long id, String name) {
super(id);
this.name = name;
}
@NonNull
@Override
public String getText(Context context) {
return null;
}
@Override
public String toString() {
return String.format("Person#%d (%s)", id, name);
}
@Override
public int compareTo(@NonNull BusinessObject o) {
if (o instanceof Person) {
System.out.println("compareTo Person");
Person po = (Person) o;
return name.compareTo(po.name);
}
else {
return super.compareTo(o);
}
}
}
public class Worker extends Person {
protected final String job;
public Worker(@Nullable Long id, String name, String job) {
super(id, name);
this.job = job;
}
@Override
public String toString() {
return String.format("Worker#%d (%s-%s)", id, name, job);
}
@Override
public int compareTo(@NonNull BusinessObject o) {
if (o instanceof Worker) {
System.out.println("compareTo Worker");
Worker wo = (Worker) o;
return String.format("%s%s", name, job).compareTo(String.format("%s%s", wo.name, wo.job));
}
else {
return super.compareTo(o);
}
}
}
Run Code Online (Sandbox Code Playgroud)
你的设计本质上是有缺陷的。
首先,你有BusinessObjectwhich 实现Comparable<BusinessObject>. 这意味着业务对象有一个自然顺序,由它们的 ID 决定。
然后重写方法Person以比较名称。你在这里说的是“好吧,如果被比较的两个业务对象都是人,那么他们的自然顺序是不同的”。
那没有意义。按 ID 排序对于业务对象来说是自然的,或者不是。但这对他们中的一些人来说是不自然的,但对另一些人来说却不是。
更正式地说,您的覆盖打破了 的传递性要求Comparable。假设您有一个由三个业务对象组成的列表:Worker Alice (ID 3)、Worker Bob (ID 1) 和 Company ACME (ID 2)。传递性说如果x < y && y < z那么x < z也一定是真的。但是,您的比较方法将给出Bob < ACME(compare by IDs) 和ACME < Alice(compare by IDs),但传递性Bob < Alice为 false (compare by names)。您违反了compareTo方法中记录的要求:
实现者还必须确保关系是可传递的:(x.compareTo(y)>0 && y.compareTo(z)>0) 意味着 x.compareTo(z)>0。
底线:Comparable不打算使用开放继承。不要那样做。相反,Comparator在有意义的地方为您的集合使用显式s。
作为旁注,这可能只是由于您的示例中的代码缩短了,但您应该始终重写equals以与compareTo(iexequals(y) 表示 x.compareTo(y) == 0,反之亦然) 保持一致。其他任何事情都意味着您的对象以不直观的方式运行。(然后您需要覆盖hashCode以履行其自己的合约 wrt equals。覆盖equals但不是hashCode将真正微妙的错误引入 Java 程序的绝妙方法。)
| 归档时间: |
|
| 查看次数: |
1729 次 |
| 最近记录: |