应该比较另一种类型吗?
我想知道以下是否有一个有效的用例:
class Base {} class A implements Comparable { //... }
它似乎是一个常见的模式(参见许多例子的集合)来接受类型T
的集合,其中T extends Comparable
T extends Comparable
。
但是在比较基类时,在技术上似乎不可能完成compareTo()
的约定,因为没有办法确保另一个类不会通过矛盾的比较来扩展基类。 请考虑以下示例:
class Base { final int foo; Base(int foo) { this.foo = foo; } } class A extends Base implements Comparable { 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 { B(int foo) { super(foo); } public int compareTo(Base that) { return -Integer.compare(this.foo, that.foo); // sort by foo descending } }
我们有两个类使用不遵循通用规则的比较来扩展Base
(如果有一个共同的规则,它几乎肯定会在Base
实现)。 然而,以下破坏的排序将编译:
Collections.sort(Arrays.asList(new A(0), new B(1)));
仅接受T extends Comparable
会不会更安全? 或者是否有一些用例可以validation通配符?
这个问题问得好。 首先,让我们从Collections
使用类似方法的原因开始
binarySearch(List extends Comparable super T>> list, T key)
的确,为什么不呢
binarySearch(List extends Comparable> list, T key)
其原因是PECS原则:Producer Extends,Consumer Super。 binarySearch
做什么? 它从列表中读取元素,然后通过将它们的值传递给compareTo
函数来比较它们。 由于它读取元素,因此列表充当生产者,因此第一部分 – 生产者扩展。 这一点很明显,那么消费者超级部分呢?
Consumer Super基本上意味着如果你只是将值传递给某个函数,你并不关心它是否接受你的对象的确切类型或它的某些超类。 所以binarySearch
声明所说的是:只要可以将任何内容传递给列表元素的compareTo
方法,我就可以查找任何内容。
在排序的情况下,它并不那么明显,因为元素只是相互比较。 但即便如此,如果Base
实际实现Comparable
(并进行整个比较), A
和B
只是扩展Base
而不用任何方式触及比较,该怎么办? 那么您将无法对A
和B
列表进行排序,因为它们分别不实现Comparable
和Comparable
。 每次子类化时都必须重新实现整个接口!
另一个例子:如果有人想在包含某些类甚至不扩展Base
实例的列表上进行二进制搜索,该怎么办?
class BaseComparable implements Comparable { 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
? super T
部分。 请注意,此类不知道密钥是A
还是B
因此无法实现Comparable
。
至于你的例子,我想这只是一个糟糕设计的例子。 不幸的是,在没有违反PECS原则和/或限制Javagenerics的function已经有限的情况下,我认为无法阻止此类事情。
约束
T extends Comparable super T>
应理解为
T extends S, S extends Comparable
Comparable
类型总是自我比较。
如果X <: Comparable
,则必须是X <: Y <: Comparable
。
我们甚至可以从compareTo()
的合同中“certificate”。 由于必须定义反向y.compareTo(x)
,因此Y <: Comparable, X<:A
必须为真。 遵循相同的论点,我们有A <: Comparable
。 然后传递性将导致A=Y