7

Even trying to come up with a clean title for this is a challenge.

The basic idea is to have two superclasses defined: one for "child" items with a member that references its "parent", the other for the "parent" lists that contain child objects. The links from child->parent and parent->child are symmetrical. Each of the parent/child superclasses have subclasses that define and implement additional functionality. There is always a parallel subclass such that child<A> pairs with parent<A>. That is, parent<A> will only contain child<A> references, and child<A> will only refer to a parent<A> - there is no "crossing over" between the subtypes.

How can I represent this? I've been stuck on this for days and the more creative I am with multi-level nested generic types the worse it gets. This is what I'd like to do:

abstract class ChildBase<T extends ChildBase<T>> {
    ParentBase<T> parent;
}
abstract class ParentBase<T extends ChildBase<T>>  {
    LinkedList<T> childList;
}
class ChildSub extends ChildBase<ChildSub> {
    // ... class specific stuff
}
class ParentSub extends ParentBase<ChildSub> {
    // ... class specific stuff
}

This is a mess. I suspect that there is a much simpler, straightforward way of doing this and it's probably in a totally different direction.

EricSchaefer
  • 25,272
  • 21
  • 67
  • 103
ags
  • 719
  • 7
  • 23
  • I'm interested to know why you require `T` to extend `ChildBase`? Is there going to be some method in those classes that requires them to be a `ChildBase`? Getting the siblings of a node is an example. If not then you could significantly simplify the structure. – sprinter Nov 13 '15 at 00:44
  • As @sprinter said perhaps a deep understanding of your problem domain is necessary to provide a better design. Consider detailing even more the application of described classes. – guilhermerama Nov 13 '15 at 01:36
  • 2
    Have you tried looking into an Interface-based solution instead of inheritance? Personally, I have found that a lot of these issues are easier to solve if you just decouple the classes instead of coupling them more. – Acapulco Dec 03 '15 at 20:52

2 Answers2

0

Did you mean class ParentSub extends ParentBase<ChildSub>?

If you make this one change, it all compiles.

This is a mess. I suspect that there is a much simpler, straightforward way of doing this and it's probably in a totally different direction.

Actually, compared to some of the other ways I've seen generics used, this isn't that bad at all. There is no radically different way of achieving this that you are missing.

The only alternative (which I think you should seriously consider) is to to make ChildBase and ParentBase non-generic, and just use casts where necessary. Generics were introduced into the language to help programmers write correct, type-safe programs. When you have complex or circular relationships between your classes, it becomes so difficult and time-consuming to work out how to use generics correctly, that the benefits of using them at all just aren't there.

Paul Boddington
  • 37,127
  • 10
  • 65
  • 116
  • Yes, I have toyed with adding the type-specific content to the subclasses (instead of casting outside of the classes). The trade-off as I see it is in the maintenance. When changes are needed I'll need to be more diligent in making sure they are propagated to all subclasses as needed; it's still true that the amount of time spend in getting the cyclic/nested generic approach to work is wasteful, and may result in fragile code that breaks every time I need to change it anyway. – ags Nov 13 '15 at 00:55
  • @ags One problem with the pattern `MyClass>` (apart from being extremely confusing) is that it's very restrictive as it doesn't mix at all well with inheritance. (http://stackoverflow.com/questions/33632871/how-to-implement-container-element-classes-by-using-type-parameters). If you decide in the future to extend `ChildSub` you'll face even worse problems because you've already fixed the type parameter as `ChildSub`. – Paul Boddington Nov 13 '15 at 01:00
0

I took a moment to analyze what you have, and I don't really see what's the problem. Your definition seems to work just fine. How is this exactly a mess?

Extending what you have to something more specific like this:

abstract class Child<T extends Child<T>> {

    private final Parent<T> parent;


    protected Child(Parent<T> parent) {
        this.parent = parent;
    }

    Parent<T> getParent() {
        return this.parent;
    }
}

abstract class Parent<T extends Child> {

    private final LinkedList<T> children = new LinkedList<>();

    void addChild(T child) {
        this.children.add(child);
    }

    List<? extends T> getChildren() {
        return Collections.unmodifiableList(this.children);
    }
}

I can easily represent the idea that you said you wanted to represent:

class Daedalus extends Parent<Icarus> { }

class Icarus extends Child<Icarus> {

    public Icarus(Parent<Icarus> parent) {
        super(parent);
    }

}

And then do

Daedalus parent = new Daedalus();
Icarus child = new Icarus(parent);
parent.addChild(child);
for(Icarus aChild : parent.getChildren()) {
    //do something with aChild
}

I didn't find this messy at all, neither it is obvious to me from your question why would this be a problem.

Edwin Dalorzo
  • 76,803
  • 25
  • 144
  • 205