我尝试为我的状态机编写一个通用的抽象类,该类将利用枚举实现在该抽象类中定义的某些接口。我有一个抽象类,其中包含一个实现通用接口的字段和一些用于状态切换逻辑的包装函数。我尝试扩展此抽象类并创建实现此泛型接口的嵌套枚举类型,但是在某些地方,我必须明确定义我将用于泛型的类型。下面的代码演示了此问题
public abstract class FiniteStateMachine<C, I> { // <- generic types declared here
private State<C, I> currentState;
protected FiniteStateMachine(State<C, I> initial){ currentState = initial; }
// some other methods for FSM, that I don't want to include in State<>
// ...
public synchronized void process(C context, I input) {
State<C, I> nextState = currentState.process(context, input)
if(currentState != nextState){
currentState.onExit(nextState, context);
State<C, I> previousState = currentState;
currentState = nextState;
nextState.onEnter(previousState, context);
}
}
public interface State<C, I> { //<- this interface should use the same types as FiniteStateMachine
State<C, I> process(C context, I input);
default void onEnter(State<C, I> s, C context) {}
default void onExit(State<C, I> s, C context) {}
}
}
class FSM extends FiniteStateMachine<Data, String> { // <- here I define types used for FSM
public FSM() { super(FSMStage.START); }
enum FSMState implements State<Data, String> { // <- and here I have to repeat them
START{
@Override
public FSMState process(Data p, String s) {
// ...
return NEXT;
},
@Override
public void onExit(State s, Data d) { /* ... */ }
},
NEXT{
// ...
}
}
}
主要问题是类型信息是在扩展类的多个位置定义的,一次是在抽象类的类型信息中,一次是在枚举实现的接口中。
FiniteStateMachine是抽象的,而不是接口,因为我需要一些标志和初始状态字段(并且除非具有受保护的构造方法,否则我不能创建“抽象字段”)。 FiniteStateMachine.State是一个接口,因为它用在无法扩展的枚举中。我还想将FiniteStateMachine和FiniteStateMachineState保存在一个文件中,因为单独的字段会在项目中创建大量的膨胀内容。同样在扩展FSM内部,onExit
方法具有一种State类型,而不是FSMStage。
我尝试了类似FiniteStateMachine<C, I, State<C, I>>
的操作,但错误提示“状态在上下文中不可访问”。
有没有办法在扩展类中的一个地方声明类型,而不是像现在那样声明FSM和FSMState?也许有一种方法可以只为FSMState声明类型,并使FSM重用这些类型?还是这种设计完全有缺陷?
答案 0 :(得分:1)
关键是内部接口(以及枚举也太隐式)是隐式静态的,因此它们无法使用外部类的通用参数,因为它们无法访问外部类的实例。
因此,基本上,此接口仅出于代码方便性而位于抽象类内部,您也可以将其提取到单独的文件中。在这种设计中,我认为没有一种方法可以跳过多个泛型类型声明。
我认为仅仅为了避免重新声明而更改设计是不值得的-您可以使State
为抽象类,并从声明中删除泛型,以便从外部类中获取泛型,但是枚举的想法不起作用。
答案 1 :(得分:0)
C
中的 FiniteStateMachine
与C
类中的State
不同,如果要使其依赖,则必须在{{ 1}}使用FiniteStateMachine
和C
与I
。您对State
几乎是正确的,但是在这里您可以做到:
FiniteStateMachine<C, I, State<C, I>>
现在您将public abstract class FiniteStateMachine<C, I, T extends FiniteStateMachine.State<C, I>> {
private T currentState;
protected FiniteStateMachine(T initial){ currentState = initial; }
public synchronized void process(C context, I input) {
FiniteStateMachine.State<C, I> nextState = currentState.process(context, input);
if(currentState != nextState){
currentState.onExit(nextState, context);
State<C, I> previousState = currentState;
currentState = (T) nextState;
nextState.onEnter(previousState, context);
}
}
public interface State<CTX, INPT> {
State<CTX, INPT> process(CTX context, INPT input);
default void onEnter(State<CTX, INPT> s, CTX context) {}
default void onExit(State<CTX, INPT> s, CTX context) {}
}
}
的类型强制为与State
中定义的类型相同。
您现在可以像这样使用它:
FiniteStateMachine
否则,重复通用类型!根据到目前为止的经验,没有办法简化类的编写。我认为您的问题的起源是当两个类在同一文件中声明时。如果将它们分成两个不同的文件怎么办?您继续这样思考吗?