Comparable 应该与另一种类型进行比较吗?

Should Comparable ever compare to another type?

我想知道以下是否有有效的用例:

class Base {}

class A implements Comparable<Base> {
    //...
}

接受类型为 T 的集合似乎是一种常见的模式(请参阅 Collections 了解一些示例),其中 T extends Comparable<? super T>.

但与基础 class 相比,在技术上似乎无法履行 compareTo() 的契约,因为无法确保另一个 class 不会扩展基础与矛盾的比较。考虑以下示例:

class Base {
    final int foo;
    Base(int foo) {
        this.foo = foo;
    }
}

class A extends Base implements Comparable<Base> {
    A(int foo) {
        super(foo);
    }
    public int compareTo(Base that) {
        return Integer.compare(this.foo, that.foo); // sort by foo ascending
    }
}

class B extends Base implements Comparable<Base> {
    B(int foo) {
        super(foo);
    }
    public int compareTo(Base that) {
        return -Integer.compare(this.foo, that.foo); // sort by foo descending
    }
}

我们有两个 classes 扩展 Base 使用不遵循通用规则的比较(如果有通用规则,它几乎肯定会在 Base 中实现).然而,以下损坏的排序将编译:

Collections.sort(Arrays.asList(new A(0), new B(1)));

只接受T extends Comparable<T>不是更安全吗?或者是否有一些用例可以验证通配符?

这是个很好的问题。首先,让我们从Collections使用

这样的方法的原因说起
binarySearch(List<? extends Comparable<? super T>> list, T key)

的确,为什么不

binarySearch(List<? extends Comparable<T>> list, T key)

这样做的原因是PECS原则:Producer Extends,Consumer Super。 binarySearch 是做什么的?它 从列表中读取 元素,然后通过 将它们的值 传递给 compareTo 函数来比较它们。由于它读取元素,因此列表充当生产者,因此第一部分 — Producer Extends。这个很明显,那么Consumer Super部分呢?

Consumer Super 基本上意味着如果您只是将值传递给某个函数,您并不真正关心它是否接受对象的确切类型或它的某些 superclass。所以 binarySearch 声明的意思是:只要任何东西都可以传递给列表元素的 compareTo 方法,我就可以寻找任何东西。

在排序的情况下并不那么明显,因为元素只是相互比较。但即便如此,如果 Base 实际上实现了 Comparable<Base>(并进行了整个比较)并且 AB 只是扩展了 Base 而没有以任何方式触及比较?那么你将无法对 AB 的列表进行排序,因为它们没有分别实现 Comparable<A>Comparable<B>。每次 subclass!

时都必须重新实现整个界面

另一个例子:如果有人想对包含某些 class 的实例的列表进行二分搜索,而这些实例甚至没有扩展您的 Base 怎么办?

class BaseComparable implements Comparable<Base> {
    private final Base key;
    // other fields

    BaseComparable(Base key, ...) {
        this.key = key;
        // other fields initialization
    }

    @Override
    public int compareTo(Base otherKey) {
        return this.key.compareTo(otherKey);
    }
};

现在他们想使用 A 的实例作为此二进制搜索的键。他们只能这样做是因为 ? super T 部分。请注意,此 class 不知道密钥是 A 还是 B,因此它不可能实现 Comparable<A/B>.

至于你的例子,我想这只是一个设计不佳的例子。不幸的是,我看不到在不违反 PECS 原则 and/or 限制已经有限的 Java 泛型功能的情况下防止此类事情发生的方法。

约束条件

    T extends Comparable<? super T>

应该理解为

    T extends S, S extends Comparable<S>

A Comparable 类型总是 self-comparing。

如果X <: Comparable<Y>,则一定是X <: Y <: Comparable<Y>

我们甚至可以 "prove" 来自 compareTo() 的合同。由于必须定义反向y.compareTo(x),因此Y <: Comparable<A>, X<:A必须为真。按照同样的论证,我们有 A <: Comparable<Y>。那么传递性会导致A=Y