确实令人困惑,因为A和B<A>这两种类型似乎相互依赖而存在;这在普通的 OOP 中没有多大意义,那它有什么用呢?我为这种模式找到了 3 个用例。
组合变成继承
假设Node 有一个子节点列表。通常的设计是通过组合来实现的
class Node
ArrayList<Node> children = ...
有时为了小的性能提升,人们使用继承来代替
class Node extends ArrayList<Node>
// the super class represents the children...
这有点令人困惑,但没有什么难以理解的。我们知道这只是为了方便,它并没有试图传达一个节点是一个节点列表。
LoadableComponent 可以考虑这个用例。可以说,它是一种不如组合方法理想的设计
class ComponentLoader<C>
C get(){...}
class EditIssue
final ComponentLoader<EditIssue> loader = new ComponentLoader<EditIssue>(){
@Override void load(){...}
@Override void isLoaded(){...}
};
EditIssue compo = ...
compo.loader.get().doSomething();
设计者可能会发现这种方法更笨拙。
方法链
而不是写
foo.doA();
foo.doB();
很多人更愿意写作
foo.doA().doB();
不幸的是,该语言并不直接支持方法链接,尽管它正在成为一种越来越受欢迎的功能。解决方法是让doA() 返回foo。它有点脏但可以接受。
但是,如果 foo 位于类型层次结构中,则解决方法会被破坏
class Bar
Bar doA()
class Foo extends Bar
Foo doB();
foo.doA().doB(); // doesn't compile, since doA() returns Bar
所以有些人呼吁一种特殊的“自我类型”来解决这个问题。假设有一个关键字This 代表“自我类型”
class Bar
This doA()
foo.doA().doB(); // works, doA() returns the type of foo, which is Foo
看来方法链是“self type”的唯一用例,所以语言可能永远不会引入它(最好直接支持方法链)
人们发现泛型可以解决这个问题
class Bar<This>
This doA()
class Foo extends Bar<Foo>
Foo has a method "Foo doA()", inherited from Bar<Foo>
这是A extends B<A> 模式最流行的用例。这是一个孤立的解决方法/技巧。它没有在 A 和 B 之间的关系中添加语义。
约束Thislike也是一种流行的做法
class Bar<This extends Bar<This>>
它又丑又没用,我强烈建议不要这样做。只需使用“This”作为约定来表明它的用途。
LoadableComponent 也可以属于这个用例。在更简单的设计中,我们可以这样做
class LoadableComponent
void ensureLoaded()
class EditIssue extends LoadableComponent
EditIssue compo = ...
compo.ensureLoaded();
compo.doSomething();
为了支持最后两行的方法链接,LoadableComponent被设计成现在的形式,这样我们就可以写成compo.get().doSomething()
更元的东西
所以前两个用例是一种黑客攻击。如果A 和B<A> 之间存在真正的约束怎么办?
B 不是作为一个普通的超类型,而是更元,它描述了一个类型A 应该有一些引用A 本身的属性。这不是传统 OOP 意义上的继承,而是更抽象的东西。 (虽然仍然是通过传统的继承机制来实现,但可以想象语言可以将其作为一个独立的概念来推广。)
Comparable 属于这个用例。它描述了某种类型可以与自己进行比较。由于它不是传统的 OOP 类型,理想情况下我们不应该使用静态类型 Comparable 声明对象。我们在公共方法返回/参数类型中看不到它,它没有多大意义。相反,我们会看到类似
<T extends Comparable<T>>
void sort(List<T>)
这里的方法需要一个符合 Comparable 模式的类型。
(我真的不知道我在这部分中在说什么)