I'm currently developing a lightweight, general-purpose simulation framework. The goal is to allow people to subclass the Simulation and Scenario objects for their domain-specific needs. Generics seemed like an appropriate way to achieve this, but I'm afraid I might be dipping into generics hell.
The Sim
object provides access to the simulation entities and controls the sim (start/pause/stop)
The Scenario
object allows you to populate the Sim with simulation entities.
Sim:
public class Sim
{
public <T extends Sim> void loadScenario(Scenario<T> scenario)
{
reset();
scenario.load(this);
}
}
Scenario:
public interface Scenario<T extends Sim>
{
public void load(T sim);
}
The goal is to allow users to create a MySim
that extends Sim
and a MyScenario
that implements Scenario<MySim>
for their domain.
e.g. MyScenario:
public class MyScenario<MySim>
{
public void load(MySim sim)
{
// make calls to sim.addMySimEntity(...)
}
}
Specifically, using the code above, the scenario.load(this)
call in Sim.loadScenario
gives me the error: The method load(T) in the type Scenario is not applicable for the arguments (Sim). I understand this is because I'm loading this
(which is of type Sim
) when what is required is T extends Sim
which means somehow I should be passing in an object that can be any subtype of Sim.
What is the way to rectify this issue to achieve what I want to accomplish? Or, is it even possible? Perhaps generics can't do this for me.
If it is always going to be a subtype of Sim then why not just specify that in your method and be done with it? It doesn't seem like generics is buying you very much here.
But anyway, I think you want loadScenario
to be:
public void loadScenario(Scenario<? extends Sim> scenario)
EDIT: Okay now I actually thought about it, that won't work either. Your loadScenario is going to get passed a Scenario with a type parameter T that is a subtype of Sim. But you are trying to pass it a Sim which has no guarantees of being the right subtype of Sim.
The solution is that the Scenario.load method needs to take a Sim viz:
public interface Scenario<T extends Sim>
{
public void load(Sim sim);
}
and Sim stays the way you wrote it. Note your MySim class must implement this and explictily narrowcast it to it's type parameter.
EDIT: Another approach is to use a static method in your Sim base class:
public class Sim
{
public static <T extends Sim> void loadScenario(Scenario<T> scenario, T sim)
{
scenario.load(sim);
}
}
It is not however possible to make this an instance method since passing an instance of Sim to something of type T extends Sim is not typesafe. By making a static method that also passes the Sim you give the compiler the opportunity to check that the T for Scenario type parameter and the T for sim match.
I understand this is because I'm loading this (which is of type Sim) when what is required is T extends Sim which means somehow I should be passing in an object that can be any subtype of Sim.
No. The problem is that accoding to the method spec, you should be passing an object of class T
, which is the specific subclass of Sim
matching your scenario - which this
is not (at least that code can't guarantee that it is).
Not sure how to fix this, though.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With