gpt4 book ai didi

Java 比较继承

转载 作者:行者123 更新时间:2023-12-01 20:22:18 24 4
gpt4 key购买 nike

我有一个基本的BusinessObject抽象类,它通过比较它们的long id字段来实现Comparable。现在想象一下,我用 Person 扩展它,然后用 Worker 扩展 Person。所以我们有:

BusinessObject <人< worker

所以现在我在 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);

通过记录,我可以看到所做的调用:

  1. Worker.compareTo()
  2. Person.compareTo()

所以,这意味着排序方法是混合的,这显然不好。那么用继承来实现 Comparable 以便调用的方法取决于集合的泛型类型的正确方法是什么呢:

  1. 如果集合是列表,则始终使用 BusinessObject.compareTo()
  2. 如果集合是列表,则始终使用 Person.compareTo()
  3. 如果集合是列表,则始终使用 Worker.compareTo()

这是我的代码:

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);
}
}
}

最佳答案

你的设计本质上是有缺陷的。

首先,你有BusinessObject它实现了 Comparable<BusinessObject> 。这说明业务对象具有由其 ID 决定的自然顺序。

然后你重写 Person 中的方法来比较名字。您在这里所说的是“好吧,如果被比较的两个业务对象都是人,那么他们的自然顺序是不同的”。

这没有道理。对于业务对象来说,按 ID 排序要么是自然的,要么不是。但这对于其中一些人来说可能是不自然的,但对于其他人来说则不然。

更正式地说,您的覆盖打破了 Comparable 的传递性要求。假设您有一个包含三个业务对象的列表: worker Alice (ID 3)、 worker Bob (ID 1) 和公司 ACME (ID 2)。传递性表示如果 x < y && y < z然后x < z也一定是真的。但是,您的比较方法将给出 Bob < ACME (按 ID 比较)和 ACME < Alice (通过 ID 进行比较),但是及物 Bob < Alice为假(按名称比较)。您违反了 compareTo 中记录的要求方法:

The implementor must also ensure that the relation is transitive: (x.compareTo(y)>0 && y.compareTo(z)>0) implies x.compareTo(z)>0.

底线:Comparable不适合与开放继承一起使用。不要那样做。相反,使用显式 Comparator为您的 Collection 提供有意义的信息。

作为旁注,这可能只是由于示例中的代码缩短所致,但您应该始终覆盖 equalscompareTo保持一致(即 x.equals(y) 意味着 x.compareTo(y) == 0,反之亦然)。其他任何情况都意味着您的对象的行为方式不直观。 (然后您需要重写 hashCode 来履行它自己的契约(Contract) w.r.t. equals 。重写 equals 但不重写 hashCode 是在 Java 程序中引入真正微妙的错误的绝佳方法。)

关于Java 比较继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44495629/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com