Java 可比继承

Java Comparable inheritance

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

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

我认为你想做的事根本做不到。 通用类型对项中使用的方法没有任何影响。

但您也许可以解决您的问题。只是不要使用 compareTo 方法,而是根据需要实现比较器。

有一个版本的 Collections.sort() 以比较器作为参数。

显然您希望根据您使用的列表使用不同的排序标准对项目进行排序。所以我认为比较器将是最好的解决方案。

您的设计本身就有缺陷。

首先,您有 BusinessObject,它实现了 Comparable<BusinessObject>。这说明企业 objects 有一个自然顺序,由他们的 ID 决定。

然后你覆盖Person中的方法来比较名称。你在这里说的是 "well, if both business objects being compared are persons, then the natural order is different for them".

这没有意义。按 ID 排序对于企业 objects 来说是自然的,或者不是。但这对他们中的一些人来说是不自然的,但对其他人来说却不是。

更正式地说,您的覆盖打破了 Comparable 的传递性要求。假设您有一个包含三个业务 objects 的列表:工作人员 Alice (ID 3)、工作人员 Bob (ID 1) 和公司 ACME (ID 2)。传递性表示如果 x < y && y < zx < z 也必须为真。但是,您的比较方法将给出 Bob < ACME(按 ID 比较)和 ACME < Alice(按 ID 比较),但可传递的 Bob < Alice 为 false(按名称比较)。您违反了 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 不适用于开放式继承。不要那样做。相反,在有意义的地方为你的 collections 使用明确的 Comparator

作为旁注,这可能只是由于示例中的代码缩短,但您应该始终覆盖 equals 以与 compareTo 保持一致(即 x.equals( y) 意味着 x.compareTo(y) == 0,反之亦然)。任何其他情况都意味着您的 objects 行为不符合直觉。 (然后你需要覆盖 hashCode 来履行它自己的合同 w.r.t。equals。覆盖 equals 而不是 hashCode 是引入真正微妙的好方法错误进入 Java 个程序。)