Java泛型双向引用

7

我试图在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){...}
}

这基本上涵盖了我所寻找的,因为我知道我可以获取一个组的子组的项并且可以获得相同类型的组。但编译器不知道如果我获取一个项的组的项,我会得到相同类型的项(例如,该项可能是孤儿)。另外,原始类型警告总是让我感觉像是在做错事情。还有,如果有更好的强制执行这种类绑定的方法,我会很感兴趣听听。


1
相关:循环泛型(尝试2) - Paul Bellora
2个回答

8
你可以尝试以下方法:
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>
> { }

然而,这些递归泛型的使用和实现往往过于复杂,很少有益处(我在这篇文章中描述了一个用例:是否有一种方法可以使用类型变量引用当前类型?)。最好退一步,重新评估设计,看看这种双向泛型关系是否真的必要。更常见的情况是,递归泛型出现在一个试图做太多事情并且其职责应该分解为多个简单类型的类型中。

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> {} 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接