我的项目中基本上有两种类型的实体,只能通过在类泛型声明中指定父目录类型来区分。目录iteself用泛型声明,因为它们可以链接到相同类型的特定旧目录。
abstract class AbstractCatalog<T extends AbstractCatalog<T>> {
public abstract T getOld();
}
class Catalog1 extends AbstractCatalog<Catalog1> {
@Override
public Catalog1 getOld() { ... }
}
class Catalog2 extends AbstractCatalog<Catalog2> {
@Override
public Catalog2 getOld() { ... }
}
到目前为止一直很好,但问题是,如果我添加一些必须包含指向某种类型目录的链接的实体,它会变得非常麻烦。
例如,
abstract class AbstractCatalogHistory<C extends AbstractCatalog<C>, E extends AbstractHistoryEntry<C, E>> {
public abstract Set<E> getEntries();
}
abstract class AbstractHistoryEntry<C extends AbstractCatalog<C>, E AbstractHistoryEntry<C, E>> {
public abstract E getPrior();
}
class Cat1HistoryEntry extends AbstractHistoryEntry<Catalog1, Cat1HistoryEntry> {
@Override
public Cat1HistoryEntry getPrior() { ... }
}
class Cat2HistoryEntry extends AbstractHistoryEntry<Catalog2, Cat2HistoryEntry> {
@Override
public Cat2HistoryEntry getPrior() { ... }
}
class Catalog1History extends AbstractCatalogHistory<Catalog1, Cat1HistoryEntry> {
@Override
public Set<Cat1HistoryEntry> getEntries() { ... }
}
class Catalog2History extends AbstractCatalogHistory<Catalog2, Cat2HistoryEntry> {
@Override
public Set<Cat2HistoryEntry> getEntries() { ... }
}
因此,在查看这样的层次结构时,要了解正在发生的事情变得更加困难。这个例子并不完整,我有几十种类型应该嵌套在我上面提供的那些中。
我这样做的目的是利用可在编译时验证的类型安全代码。但与此同时,这样的代码变得非常混乱,因为我必须在向层次结构中添加新类型时指定更长的泛型链。
有没有办法处理此类仿制药爆炸?
答案 0 :(得分:2)
您的示例并未完全清楚为什么您需要为Catalog1
和Catalog2
设置单独的类,但我们假设已设置此类。
然而,即使如此,我也没有理由为什么其他引用这些目录的内容都需要这种重复。如果您只是想确保它与正确的目录类型相关联,那么这是您真正需要的唯一通用参数:
class CatalogHistory<C extends AbstractCatalog<C>> {
public Set<HistoryEntry<C>> getEntries();
}
class HistoryEntry<C extends AbstractCatalog<C>> {
public HistoryEntry<C> getPrior();
}
但是如果你实际上做了不同的事情,例如Cat1HistoryEntry
和Cat2HistoryEntry
所以您需要单独的课程?在这种情况下,您显然无法使用抽象基类和两个具体实现,但我认为不需要引入泛型类型,然后按照您的方式将它们分解为具体类型:
abstract class AbstractHistoryEntry<C extends AbstractCatalog<C>> {
public abstract AbstractHistoryEntry<C> getPrior();
}
class Cat1HistoryEntry extends AbstractHistoryEntry<Catalog1> {
@Override
public Cat1HistoryEntry getPrior() { ... }
}
class Cat2HistoryEntry extends AbstractHistoryEntry<Catalog2> {
@Override
public Cat2HistoryEntry getPrior() { ... }
}
这里有一些事情发生。首先,考虑AbstractHistoryEntry
。如果您有其中一个,那么您正在开发一个通用级别,而不应该关心getPrior
返回这个或那个具体的子类型 - 您需要知道的是它返回引用它的另一个AbstractHistoryEntry
对象目录。
如果您有一个具体的Cat1HistoryEntry
引用,那么由于Java中返回类型的协方差,您仍然可以获得从Cat1HistoryEntry
获得另一个getPrior
的完全类型安全性。 / p>
现在它变得稍微复杂了 - 让我们试着用AbstractCatalogHistory
拉出相同的技巧:
abstract class AbstractCatalogHistory<C extends AbstractCatalog<C>> {
public abstract Set<? extends AbstractHistoryEntry<C>> getEntries();
}
class Catalog1History extends AbstractCatalogHistory<Catalog1> {
@Override
public Set<Cat1HistoryEntry> getEntries() { ... }
}
class Catalog2History extends AbstractCatalogHistory<Catalog2> {
@Override
public Set<Cat2HistoryEntry> getEntries() { ... }
}
如您所见,两个具体子类仍然返回一组具体类型Cat1HistoryEntry
和Cat2HistoryEntry
。抽象基类型现在需要为这些集表达一个公共超类型,以便您可以以通用方式处理结果。这是通过引入协方差来完成的。
<强> 塞特斯 强>
塞特斯有点复杂化了这个问题。基本上,如果您有一个像List<T>
或AbstractCatalogHistory<C>
这样的通用容器/集合,并且您希望同时允许添加和检索项目,那么如果您想要类型,则不能再在项目类型中出现差异安全
例如,如果您在AbstractCatalogHistory<C>
中设置了一个允许您将任何AbstractHistoryEntry<C>
项添加到历史记录中的setter,那么您就会遇到问题,因为如果您的AbstractCatalogHistory<C>
实际上是Catalog1History
那么你只需要Cat1HistoryEntry
个项目!
这与通用列表存在同样的问题:List<Cat>
不是List<Mammal>
,因为您可以将大象添加到List<Mammal>
,但您无法添加大象到List<Cat>
。
如果您坚持Catalog1
的历史记录必须只包含Cat1HistoryEntry
个项目,那么解决方案就是只将{b}添加到Catalog1History
,而不添加AbstractCatalogHistory<C>
}。通过这种方式,泛型类只能用于读取历史记录,而不能用于编写历史记录。
然而,回到我的回答的开头:如果你实际上不需要双具体类,解决方案仍然非常简单。不幸的是,你仍然没有解释为什么或如果你需要那些。如果你真正想要的是Catalog1
/ Catalog2
区别,那么你实际上并不需要一个不同的实现,例如Cat1HistoryEntry
和Cat2HistoryEntry
,则以下内容就足够了:
class CatalogHistory<C extends AbstractCatalog<C>> {
public Set<HistoryEntry<C>> getEntries();
public void addEntry(HistoryEntry<C> entry);
}
class HistoryEntry<C extends AbstractCatalog<C>> {
public HistoryEntry<C> getPrior();
public void setPrior(HistoryEntry<C> prior);
}