Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use generics with multiple coupled objects?

So I'm having trouble wrapping my head around the proper design for this.

My application has two key objects that control state, that need to interact with one another: ItemList, ItemState. These each rely on a generic ITEM_TYPE so they can function in different contexts. They are also abstract to allow for ITEM_TYPE-dependent behavior.

Both pieces need to know the generic type, but moreover, since they talk to one another, they need to know the generic types of one another. (An ItemList< String > instance needs to know that its ItemState field is an ItemState< String > and vice versa).

My current solution works, but it seems awful. There has to be a better way. This is what I'm doing now:

public abstract class ItemState<
  ITEM_TYPE,
  STATE_TYPE extends ItemState<ITEM_TYPE, STATE_TYPE, LIST_TYPE>,
  LIST_TYPE extends ItemList<ITEM_TYPE, STATE_TYPE, LIST_TYPE>> {

}
public abstract class ItemList<
  ITEM_TYPE,
  STATE_TYPE extends ItemState<ITEM_TYPE, STATE_TYPE, LIST_TYPE>,
  LIST_TYPE extends ItemList<ITEM_TYPE, STATE_TYPE, LIST_TYPE>> {

}

Then an implementing class might look like:

class StringState extends ItemState<String, StringState, StringList> {

}
class StringList extends ItemList<String, StringState, StringList> {

}

Note that for ItemState, STATE_TYPE is a reference back to the implementing class, and likewise for ItemList/LIST_TYPE.

Really my problem would be solved if I just make ItemState an inner class of ItemList since there would be an implicit binding and they could share generic declarations, but both classes are so large and standalone, that I would prefer not to do this.

Any suggestions?

Edit: As a counter-example to a comment:

public abstract class ItemState<ITEM_TYPE> {  
  public abstract ItemList getItemList();  
  public void doSomething() {
    // This should not compile because abstract super class has
    // no idea what the generic type of getItemList() is
    ITEM_TYPE item = this.getItemList.getItem(); 
  }  
}

Edit 2: I think the best solution I could think of was just to make ItemList/ItemState inherit one way or the other so they can function as the same class. I don't love this solution because it overrides separation of concerns, but it makes the generics a lot more manageable.

Sidenote: my actual applicaiton had this problem with 4 intertwined classes, I just used 2 for simplicity. In actuality the generics were so bad they were incomprehensible and hard to refactor (about 4 entire lines of just generic declarations for each class). I've now made these 4 classes into a vertical inheritance hierarchy

JM Yang's solution is pretty good

like image 557
sicklybeans Avatar asked Jul 11 '26 08:07

sicklybeans


1 Answers

I think you may just reference to generic type ITEM_TYPE when declaring these 2 classes.

I'm able to compile below code with no errors.

public abstract class ItemList<ITEM_TYPE> {
    public abstract ItemState<ITEM_TYPE> getState();

    public abstract ITEM_TYPE getItem();
}

public abstract class ItemState<ITEM_TYPE> {
    public abstract ItemList<ITEM_TYPE> getItemList();

    public void doSomething() {
        ITEM_TYPE item = getItemList().getItem();
        System.out.println(item);
    }
}

public class StringList extends ItemList<String> {
    @Override
    public StringState getState() {
        return new StringState();
    }

    @Override
    public String getItem() {
        return "";
    }
}

public class StringState extends ItemState<String> {
    @Override
    public StringList getItemList() {
        return new StringList();
    }
}
like image 51
JM Yang Avatar answered Jul 15 '26 22:07

JM Yang