Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Generics Hell - Passing in this but it wants type T

Tags:

java

generics

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.

like image 260
Brad Avatar asked Apr 20 '10 14:04

Brad


2 Answers

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.

like image 175
Dean Povey Avatar answered Oct 06 '22 00:10

Dean Povey


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.

like image 30
Michael Borgwardt Avatar answered Oct 05 '22 23:10

Michael Borgwardt