3

I'm making a framework in Java and have a method in my abstract class, BasicPhase, called processAction (snippet below). It takes two abstract classes as parameters since the user should implement them in child classes. However, it results in the user being required to constantly downcast every time they override the method. How can this be avoided?

BasicPhase.java

public class BasicPhase {
    ...
    public BasicGameState processAction(BasicAction action, BasicGameState state);
    ...
}

Example User Class

MyPhase.java

public class MyPhase extends BasicPhase {
    @Override
    public BasicGameState processAction(BasicAction basicAction, BasicGameState basicState)  {
        MyGameState state = (MyGameState) basicState; //undesired
        MyAction action = (MyAction) basicAction; //undesired

        //Game Logic Here
    }
}
2
  • 3
    As you are showing it, there is no way for a compiler or a programmer to ensure or validate that processAction invoked on a MyPhase instance is being passed a MyGameState action. Thus, the casts are strictly necessary, and, may fail at runtime. There is not enough surrounding context to understand how to address this. The context required goes to who calls processAction, and what they do with the state and actions. I think that any potential solution will involve refactoring the invocation of this method and where the state is stored/passed, maybe applying a design pattern like Strategy.
    – Erik Eidt
    Commented Jan 1, 2016 at 18:46
  • what you're looking for is a generic class to inherit where the generic parameters are the types of action and game state the phase accepts - processAction clearly cannot work on the base action and state types, so you need to make the types that need to be passed as parameters strongly specified. Do this by making BasicPhase a BasicPhase<TAction, TState> and the processAction a processAction(TAction action, TState state) Commented Jan 1, 2016 at 20:40

2 Answers 2

5

processAction clearly cannot work on the base action and state types, so you need to make the types that need to be passed as parameters specifically the types that processAction can work with. To make the parameter types definable by your derived classes, you need to make the base class generic such that the generic parameters are the types of action and game state the phase accepts.

Do this by making BasicPhase a

public class BasicPhase<TAction extends BasicAction, TState extends BasicGameState>

and the processAction a processAction(TAction action, TState state) then your MyPhase would derive as:

public class MyPhase extends BasicPhase<MyAction, MyGameState> {
3
  • Hm, from what the OP wrote I assume he wants his framework to hold a list of BasicPhase (or derived) objects which is created at run time and call processAction for each of the objects in the list. I think that will be not so easy with your suggestion.
    – Doc Brown
    Commented Jan 1, 2016 at 22:16
  • @DocBrown while I agree you the consumer would need to know the type of the phase to execute it in this case (it would need to be a List<BasicPhase<MyAction, MyGameState>>) I don't see any mention of the list in his question - also you have to realize whoever's calling processAction already has knowledge of MyAction and MyGameState and therefore must know it's calling a MyPhase because otherwise the processAction will throw an exception on the downcast.. Commented Jan 1, 2016 at 22:21
  • I agree, we are guessing around here how the code the OP did not show us might look like - maybe the lack of information is leading us to wrong assumptions.
    – Doc Brown
    Commented Jan 1, 2016 at 22:26
4

If MyPhase.progressAction would exclusively call methods available in BasicAction or BasicGameState, then there would be no need for any downcast. Those methods might be ordinary methods or methods overridden in MyAction and/or MyGameState. So you should consider to change the design of BasicAction, BasicGameState and/or progressAction to make this possible.

Note, as @ErikEidt correctly stated in his comment, that this may not be the best or most sensible solution for your case, since you gave us not enough information to allow any better advice.

1
  • I came back to write just this. Damn you for stealing my upvote (although I'm the upvoter here)!
    – MetaFight
    Commented Jan 1, 2016 at 20:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.