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?