Search code examples
optaplanner

CustomVariableListener with sources of different entities - Parameterization of Listener


Recently, a question was asked here how to implement a listener where the source variables are on different planning entity classes, e.g.

@PlanningEntity
class FirstPlanningEntityClass{

  @PlanningVariable
  int a;
}

and

@PlanningEntity
class SecondPlanningEntityClass{

  @PlanningVariable
  int b;
}

In my third (shadow) planning entity I want to update my shadow planning variable when either a or b changes (i.e., a and b as sources), such as:

@PlanningEntity //shadow planning entity
class ShadowEntityClass{
  @CustomShadowVariable(variableListenerClass = MyListener.class,
        sources = {@CustomShadowVariable.Source(entityClass = FirstEntityClass.class, variableName = "a"),
                @CustomShadowVariable.Source(entityClass = SecondEntityClass.class, variableName = "b")})
  public local int getMyShadowVariable(){...}

However, MyListenerClass must implement a parameterized VariableListener with exactly one type parameter, where the type parameter corresponds to the entity class which the source variable is on:

public class MyListenerClass implements VariableListener<Entity_>{...}

But the source variables are on two planning entity classes.

To solve the problem, I tried several things:

  • I can't interface FirstPlanningEntityClass and FirstPlanningEntityClass with e.g. a common CommonPlanningEntityInferface as the two entity classes are wildly different. This common interface could have been used as the type parameter for VariableListener.
  • I also cannot specify two different listener classes in the @CustomShadowVariable argument list, such as

    @CustomShadowVariable(variableListenerClass = MyFirstListener.class,
    sources = {@CustomShadowVariable.Source(entityClass = FirstPlanningEntity.class, variableName = "a")}, 
    variableListenerClass = MySecondListener.class, 
    sources = {@CustomShadowVariable.Source(entityClass = SecondPlanningEntity.class, variableName = "b")})
    
  • or two annotations on top of one another, e.g.

    @CustomShadowVariable(variableListenerClass = MyFirstListener.class,
    sources = {@CustomShadowVariable.Source(entityClass = FirstPlanningEntity.class, variableName = "a")})
    @CustomShadowVariable(variableListenerClass = MySecondListener.class,
    sources = {@CustomShadowVariable.Source(entityClass = SEcondPlanningEntity.class, variableName = "b")})
    

The manual only refers to many planning variables of the same class or to one planning variable of a different class (section 4.3.6.4., 6.5.0).

So how can I implement a shadow variable which has as source two planning variables from different planning entity classes? The answer given here mostly covered the change of two shadow variables in one planning entity class.


Solution

  • Use a common interface or just plain old java.lang.Object as the generic parameter type:

    public class MyListenerClass implements VariableListener<Object>{...}
    

    Your implementation will probably need instanceof checks on the source instance and cast to the first or second class accordingly.