庞大的Java类层次结构中繁琐的泛型声明

约翰·杜

我项目中的每个实体基本上都有两种类型,仅通过在类泛型声明中指定父目录类型才能区分。目录本身用泛型声明,因为它们可以链接到相同类型的特定旧目录。

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() { ... }
}

因此,在查看这样的层次结构时,要想知道正在发生的事情要困难得多。这个例子绝不是完整的,我应该在我上面提供的类型中嵌套许多类型。

我试图通过这样做来利用可以在编译时进行验证的类型安全代码的优势。但是同时,这样的代码变得非常混乱,因为我必须指定更长的泛型链,同时向层次结构中添加新的类型。

有没有办法处理这种泛型爆炸?

梅多42

您的示例并不能完全清楚为什么需要为Catalog1Catalog2设置单独的类,但让我们假设已设置了该类。

但是,即使如此,我仍然没有理由为什么引用这些目录的其他所有内容都需要这种重复。如果只想确保它与正确的目录类型相关联,那么这是您真正需要的唯一通用参数:

class CatalogHistory<C extends AbstractCatalog<C>> {
    public Set<HistoryEntry<C>> getEntries();
}

class HistoryEntry<C extends AbstractCatalog<C>> {
    public HistoryEntry<C> getPrior();
}

但是,如果您实际上在eg中做不同的事情Cat1HistoryEntryCat2HistoryEntry那么您需要单独的类怎么办?在那种情况下,您显然无法避免使用抽象基类和两个具体实现,但是我认为无需引入泛型类型,然后按照您的方式将它们固定为具体类型:

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参考,但是,你仍然可以得到得到另一个全类型安全Cat1HistoryEntry出来的getPrior感谢返回类型的Java中的协方差。

现在变得稍微复杂了-让我们尝试使用以下方法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() { ... }
}

如您所见,两个具体的子类仍返回一组具体类型Cat1HistoryEntryCat2HistoryEntry现在,抽象基类型需要为这些集合表示一个公共超类型,以便您可以以通用方式使用结果。这是通过引入协方差来完成的。

二传手

二传手使事情复杂化了。基本上,如果您有一个通用容器/集合(例如List<T>或)AbstractCatalogHistory<C>,并且您希望同时允许添加和检索项目,那么如果您要保证类型安全,就不能再有项目类型的差异。

例如,如果您有一个AbstractCatalogHistory<C>允许AbstractHistoryEntry<C>在历史记录中添加任何项目的设置器,那么您就会遇到问题,因为如果您AbstractCatalogHistory<C>实际上是a,Catalog1History那么您只希望其中有Cat1HistoryEntry项目!

这与通用列表存在相同的问题:AList<Cat>不是a,List<Mammal>因为您可以向a添加大象List<Mammal>,但是您不应该向a添加大象List<Cat>

如果您坚持的历史记录Catalog1必须仅包含Cat1HistoryEntry项目,则一种解决方案是仅将setter添加到中Catalog1History而不添加AbstractCatalogHistory<C>这样,泛型类将仅用于读取历史记录,而不用于编写历史记录。

但是,回到我的答案的开头:如果您实际上不需要双重具体类,则解决方案仍然非常简单。不幸的是,您仍然没有解释为什么或是否需要这些。如果您真正想要的只是Catalog1/Catalog2区别,并且实际上不需要为egCat1HistoryEntry进行其他实现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);
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

Java泛型层次结构

来自分类Dev

Kotlin泛型在类层次结构中不匹配

来自分类Dev

Java类层次结构中与泛型不兼容的空性约束

来自分类Dev

是否可以映射(JPA)在抽象超类中使用泛型的Java类型层次结构?

来自分类Dev

并行类层次结构上下文中的Java泛型问题

来自分类Dev

泛型:如何处理类层次结构中的多个有界类型

来自分类Dev

层次结构和泛型

来自分类Dev

当参数化类型通过层次结构传递时,在Java中创建泛型类型的实例吗?

来自分类Dev

类声明中的泛型类型

来自分类Dev

Java中的泛型类

来自分类Dev

在泛型类中实例化 Java 泛型类

来自分类Dev

Java泛型类中的超类

来自分类Dev

泛型类声明的澄清

来自分类Dev

声明泛型类的变量

来自分类Dev

在类中声明类型参数时,Java泛型类型会丢失常量类型

来自分类Dev

如何使用泛型类型声明类 - Java 1.7

来自分类Dev

Java中的泛型超类

来自分类Dev

java类中的多个泛型类型

来自分类Dev

在java中扩展多个类的泛型

来自分类Dev

Java @与类层次结构竞争

来自分类Dev

Ruby中的类层次结构

来自分类Dev

TypeScript 中的类层次结构

来自分类Dev

方法中的泛型声明

来自分类Dev

在Java级别中为形状类创建层次结构

来自分类Dev

Java中具有泛型声明的ArrayList

来自分类Dev

Java泛型:方法声明参数中的类型扩展

来自分类Dev

用Java中的复杂类声明泛型变量

来自分类Dev

使用泛型链接两个层次结构

来自分类Dev

泛型类型如何适合类型层次结构?