我在某处看到了这种模式:
class A extends B<A> {
}
通过将新类型指定为泛型的实际类型,此结构扩展泛型有点不寻常。有什么用?这个模式有名字吗?有没有替代模式?
示例:https://code.google.com/p/selenium/wiki/LoadableComponent
跳转至:public class EditIssue extends LoadableComponent<EditIssue> {
编辑: 阅读完回复后,我似乎需要改变编译器对类型检查的理解。在我的脑海里,我的牛肉有这种模式,如果两个A需要相同,那么有没有办法不重复它们?但似乎没有更好的方法将派生类的类型传播给父类。
答案 0 :(得分:4)
在处理recursive data structures时,您可能会这样做。例如,图形或树中的节点可以定义为其他节点的集合:
class Node extends AbstractList<Node> {
...
}
如果抽象/泛型类型用于比较相似类型的对象,例如java.lang.Comparable
的情况,则可能会看到类似这样的内容:
class MyObject implements Comparable<MyObject> {
public int compareTo(MyObject other) { ... }
}
答案 1 :(得分:4)
当然,OOP的答案是A
是B
。如果A
不是B
而不是A
,则应该只使用B
来构建B
以使用B
的功能。
据推测,B
也有一些通用实现,它们利用了对泛型类型的限制。
另一个用例是abstract class B<T extends B<T>> {
public T createCopy(T t);
}
看起来像:
createCopy
现在,子类可以实现class A extends B<A> {
public A createCopy(A t) {
return new A(t); //copy constructor
}
}
,客户端代码可以安全地使用它而无需强制转换......例如。
abstract class B {
public B createCopy(B t);
}
class A extends B {
public B createCopy(B t) { //Is the copy an A or a different subtype of B? We don't know.
return new A(t); //copy constructor
}
}
将上述内容与:
进行比较{{1}}
答案 2 :(得分:2)
举个例子:
E extends Comparable<E>
这意味着E必须是一个知道如何与自身进行比较的类型,因此,递归类型定义。
不知道它是否有任何正式名称,但我会称之为递归泛型类型。
答案 3 :(得分:1)
此模式与任何其他子类相同。使用泛型时真正发生的事情是JVM正在创建一个类的副本(实际上不是副本,但它有点类似),并用指定的类型替换泛型所使用的所有点。
所以,为了回答你的问题,所有这种模式都在用B<A>
代替B
,其中A
的所有用法都被任何类A
所取代。 。这种情况的潜在用途是您为特定类定制数据结构(来自java.util.Collections
),例如使用位移将Collection<Boolean>
压缩为较小的内存量。我希望这是有道理的!
答案 4 :(得分:1)
确实令人困惑,因为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
似乎方法链是“自我类型”的唯一用例,因此语言可能永远不会引入它(最好直接支持方法链接)
人们发现泛型提供了解决此问题的方法
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之间的关系中没有添加语义。
限制This
之类的
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模式的类型。
(我真的不知道我在这节谈论的是什么)