How can I properly make a generic type of another generic type in java?

Viewed 34

I have many classes that include each other like such: a Project contains many Tabs, each of which contain many Items.

Items' hierarchy is pretty straightforward:

abstract Item
  abstract FooItem extends Item
    FooAItem extends FooItem
    FooBItem extends FooItem
  abstract BarItem extends Item
    BarAItem extends BarItem
    BarBItem extends BarItem

Tabs need to know what type of Items they can contain:

abstract Tab<ITEM extends Item>
  abstract FooTab<ITEM extends FooItem> extends Tab<ITEM>
    FooATab extends FooTab<FooAItem>
    FooBTab extends FooTab<FooBItem>
  abstract BarTab<ITEM extends BarItem> extends Tab<ITEM>
    BarATab extends BarTab<BarAItem>
    BarBTab extends BarTab<BarBItem>

So far, I'm good. It becomes tricky when I add the Project layer which only has 1 generic implementation (and I can't create more specific implementations because of legacy code):

Project<ITEM extends Item, TAB extends Tab<ITEM>>

Now the type of Tab becomes linked to the type of Item. So in my code, when I use a variable of type, let's say, Project<FooItem, FooTab<FooItem>>, I can't write a method project.getTab(id) that could be called like:

FooATab fooATab = project.getTab(id);

Because the resulting type does not cast to the generic type TAB extends Tab<ITEM> where ITEM is FooItem and not the FooAItem specified in the signature of FooATab.

Here's some partial implementation of Project as of today:

public class Project<ITEM extends Item, TAB extends Tab<ITEM>> {

    private final SortedSet<TAB> tabs;

    public <T extends TAB> T getTab(final int id) {
        return this.tabs.stream()
                          .filter(t -> id == t.getId())
                          .findAny()
                          .map(a -> (T) a)
                          .orElse(null);
    }
}

(I also get an unchecked cast from TAB to T warning, but that's kinda expected).

Any hints on what I'm doing wrong?

0 Answers
Related