tags:

views:

175

answers:

2

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.

+4  A: 

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.

Dean Povey
Surely you mean `load(T sim)` in the last case? Else the generic parameter on the class is less than useful... :-)
Andrzej Doyle
@Andrzej No, I believe he meant load(Sim sim). Indeed, the generic on the class is no longer useful. It feels like I would not be able to accomplish what I'd like to do.
Brad
@Andrzej and @Brad. Correct, the generic is not that useful which was the point of the first part of the comment :-) Normal polymorphism should be as effective here.
Dean Povey
@Dean The question however still remains. Is it possible to use generics to accomplish this so I do not have to force a cast at run-time?
Brad
@Brad. You can't do it the way you have done. It is possible as a static method. See Edit.
Dean Povey
+2  A: 

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.

Michael Borgwardt