Reputation: 284
The problem I am having is quite specific, and a bit difficult to explain. Let me know if you need more details about anything. I have an abstract class called System
. To hold my System
objects, I have a SystemManager
which contains an list of System
s, and some functions for manipulating it. Inside it contains:
List<System> systems = new ArrayList<System>();
Now, I want to create another abstract class which is a specific type of System
called RenderSystem
. This will inherit from System
but have a few more functions. I also want to create a RenderSystemManager
which should do everything SystemManager
does, except with a few extra features. Also, instead of having a list of System
in the manager, I would like it to have a list of RenderSystem
to ensure that the programmers don't put any regular System
objects in it. My initial instinct was to inherit SystemManger
, and just change the type of the list to RenderSystem
:
systems = new ArrayList<RenderSystem>();
Java doesn't allow this as systems is type System
not RenderSystem
. I would have assumed it would be OK considering RenderSystem
inherits from System
. One way I can think of to resolve this issue is to copy and paste all the code from SystemManager
into RenderSystemManager
and just change the line of code to be:
List<RenderSystem> systems = new ArrayList<RenderSystem>();
My other instinct would be to override the addSystem(System system)
function to ensure that it only handles RenderSystem
, but the programmers might think they are allowed to do it even if it doesn't work.
@Override
public void addSystem(System system)
{
if (system instanceof RenderSystem)
{
super.addSystem(system);
}
}
These doesn't seem very elegant though. Anybody have any suggestions?
Upvotes: 0
Views: 62
Reputation: 13123
Your SystemManager could have a a list of System objects, and the list could be private, and the only way to add an object to that list would be a function that only took a RenderSystem as an argument. You're trying to manhandle generics into a use for which they probably are not appropriate.
But I think you have bigger problems.
I think this happens to many of us when we start trying to design "from the inside out", i.e., you are taking programming constructs and trying to string them together at a level of detail that ignores (or forgets) what the code is trying to do from a higher level. It's like saying "I want a while loop inside a do loop that has a switch statement with try-catch-finally-whatever, but I don't want to nest all these damn braces."
Take a few steps back and think about the external functionality you want to accomplish, and progress in small steps through design and implementation details from there...
Upvotes: 0
Reputation: 691735
Your managers have the same type-safety requirements as the list they wrap. They should thus follow the same strategy, and be generic types:
public class BaseSystemManager<T extends System> {
private List<T> systems = new ArrayList<>();
public void addSystem(T system) {
systems.add(system);
}
// common methods
}
public class SystemManager extends BaseSystemManager<System> {
// methods specific to System handling
}
public RenderSystemManager extends BaseSystemManager<RenderSystem> {
// methods specific to RenderSystem handling
}
Upvotes: 5
Reputation: 2542
I think your second instinct to add protection into the addSystem call is the correct one. That way SystemManager can still operate on the list of Systems. However I would change the implementation of addSystem to instruct developers in the proper usage:
@Override
public void addSystem(System system)
{
if (system instanceof RenderSystem)
{
super.addSystem(system);
}
else
{
throw new IllegalArgumentException("Only RenderSystem objects can be added to a RenderSystemManager");
}
}
Upvotes: 0