【问题标题】:Java generics 2-way referenceJava 泛型 2-way 参考
【发布时间】:2014-01-11 00:55:57
【问题描述】:

我试图在 java 中创建两个相互包含的类之间的泛型关系。这些对象基本上形成了一个交替的层树。到目前为止,我发现的最接近的 SO 问题是:Java generics of generics of,这很接近并且对我的问题有所帮助,但仍然有足够的不同,我需要额外的指导。这是情况,或者更确切地说,是我想要的情况:

abstract class Group<I extends Item<Group<I>>>{
     private List<I> items;
     public List<I> getItems(){...}
     public void setItems(List<I> items){...}
}

abstract class Item<G extends Group<Item<G>>>{
     private List<G> subGroups;
     public List<G> getSubGroups(){...}
     public void setSubGroups(List<G> subGroups){...}
}

除了 getter 和 setter 之外,类的某些方面使它们彼此显着不同,但包含应该遵循这样。这背后的原因是我想强制执行,如果我有实现类,它们必须表现得像这样:

class AGroup extends Group<AItem>{...} //works
class AItem extends Item<AGroup>{...} //works
class BGroup extends Group<BItem>{...} //works
class BItem extends Item<BGroup>{...} //works
class MixedGroup extends Group<AItem>{...} //fails since AItem does
                                           //not extend Item<MixedGroup>

到目前为止,编译器都很好

abstract class Group<I extends Item<Group<I>>>{
     private List<I> items;
     public List<I> getItems(){...}
     public void setItems(List<I> items){...}
}

abstract class Item<G extends Group>{ //raw types warning
     private List<G> subGroups;
     public List<G> getSubGroups(){...}
     public void setSubGroups(List<G> subGroups){...}
}

这主要涵盖了我正在寻找的内容,因为我可以知道我可以获得一个组的项目的子组并返回相同类型的组。但是编译器不知道如果我得到一个项目的组的项目,我会得到相同类型的项目(例如,项目可能是一个孤儿)。此外,原始类型警告总是让我觉得我做错了什么。另外,如果有更好的方法来执行这种类绑定,我很想听听。

【问题讨论】:

标签: java generics


【解决方案1】:

您可以尝试以下方法:

abstract class Group<I extends Item<I, G>, G extends Group<I, G>> {
     private List<I> items;
     public List<I> getItems() { return null; }
     public void setItems(List<I> items) { }
}

abstract class Item<I extends Item<I, G>, G extends Group<I, G>> {
     private List<G> subGroups;
     public List<G> getSubGroups() { return null; }
     public void setSubGroups(List<G> subGroups) { }
}

class AGroup extends Group<AItem, AGroup> { }         // works
class AItem extends Item<AItem, AGroup> { }           // works
class BGroup extends Group<BItem, BGroup> { }         // works
class BItem extends Item<BItem, BGroup> { }           // works
class MixedGroup extends Group<AItem, MixedGroup> { } // fails

(ideone)

使用两个类型参数的原因是,由于每个类型都使用相反的类型参数化,因此每个类型都需要跟踪对方的类型和自己的“自身类型”。

这可以推广到任意数量的“参与”类型:

// one type
interface SelfParameterized<T extends SelfParameterized<T>> { }

// two types
interface SelfParameterizedPairA<
        A extends SelfParameterizedPairA<A, B>,
        B extends SelfParameterizedPairB<A, B>
> { }
interface SelfParameterizedPairB<
        A extends SelfParameterizedPairA<A, B>,
        B extends SelfParameterizedPairB<A, B>
> { }

// three types
interface SelfParameterizedTrioA<
        A extends SelfParameterizedTrioA<A, B, C>,
        B extends SelfParameterizedTrioB<A, B, C>,
        C extends SelfParameterizedTrioC<A, B, C>
> { }
interface SelfParameterizedTrioB<
        A extends SelfParameterizedTrioA<A, B, C>,
        B extends SelfParameterizedTrioB<A, B, C>,
        C extends SelfParameterizedTrioC<A, B, C>
> { }
interface SelfParameterizedTrioC<
        A extends SelfParameterizedTrioA<A, B, C>,
        B extends SelfParameterizedTrioB<A, B, C>,
        C extends SelfParameterizedTrioC<A, B, C>
> { }

然而,这类递归泛型的使用和实现往往过于复杂,而且很少有好处(我在这篇文章中描述了一个用例:Is there a way to refer to the current type with a type variable?)。最好退后一步重新评估您的设计,看看这种双向通用关系是否真的有必要。 More often than not 我发现递归泛型源于一种试图做太多事情并且其职责应该被解耦为多个更简单类型的类型。

【讨论】:

    【解决方案2】:

    如果您像这样定义组和项目,它应该会让您更进一步:

    abstract class Group<I extends Item<? extends Group<I>>>
    
    abstract class Item<G extends Group<? extends Item<G>>>
    

    这应该会产生以下结果:

    class AGroup extends Group<AItem>{}
    class AItem extends Item<AGroup>{}
    
    //doesn't work since the item could only be added to MixedGroup instances
    //but MixedGroup only accepts AItem instances
    class MixedItem extends Item<MixedGroup>{} 
    
    //works since the item might be added to any AGroup
    class MixedItem2 extends Item<AGroup>{} 
    
    //works, since AItem can be added to any AGroup (and MixedGroup is a Subclass)
    class MixedGroup extends Group<AItem> {} 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-05-08
      • 2011-10-14
      • 1970-01-01
      • 1970-01-01
      • 2022-01-25
      • 2010-12-18
      相关资源
      最近更新 更多