我的最终目标是拥有一个StateTransitionTable
,客户端可以在其中创建一个由mainState
组成的条目,并声明它可以从该主要状态跳转到({1}中存储})。此应用程序ArrayList
在循环上运行,转换表应该在某种程度上自我操作。
请不要建议我使用enum;它们不能很好地扩展
每个州都应该有权访问Script
和Script
个实例。该脚本用于执行它的任务,该表将使用索引转换到下一个状态。索引取决于转换添加到条目的顺序。创建的第一个条目是第一个使用的条目。
在应用程序的循环中,应该访问存储在当前条目中的状态,并且应该从中调用TransitionTable
。 process
调用Script
,传入当前脚本实例:
<T extends Script> process(T)
该方法从//this method is called repeatedly
public void loop() {
table.process(this);
}
抓取mainState
并调用currentEntry
方法,传入process(T, TransitionTable)
实例和当前表实例。在州的流程方法中,我应该能够使用Script
参数访问有关脚本的信息,并使用该表进行转换。似乎我有一切都按顺序排列,除了1个声明给我一个错误。
script
TransitionTable:
public interface State<T extends Script> {
void process(T script, TransitionTable table);
}
对于记录,public class TransitionTable {
private Map<State<?>, StateNode> entries = new HashMap<>();
private StateNode currentNode, startNode;
public <T extends Script> void process(T script) {
currentNode.mainState.process(script, this); //Compile-time error here
}
public StateNode createEntry(State<?> state) {
StateNode node = new StateNode(state);
map.put(state, node);
if(startNode == null)
startNode = currentNode = node;
return node;
}
public void transitionTo(int index) {
State<?> nextState = currentNode.states.get(index);
if(nextState == null)
nextNode = startNode.mainState;
currentNode = entries.get(nextNode);
}
public static final class StateNode {
private ArrayList<State<?>> states = new ArrayList<>;
private State<?> mainState;
public StateNode(State<?> state) {
mainState = state;
}
public StateNode addTransition(State<?> state) {
states.add(state);
return this;
}
}
}
的泛型类型声明是允许使用实际脚本类型的廉价黑客。虽然可以输入不同的类型,但它始终是相同的类型。便宜的黑客。
我得到的错误是:
类型State中的方法进程(捕获#4-of?,StateTransitionTable)不适用于参数(T,StateTransitionTable)
我理解这背后的原因:客户端有可能为方法的类型参数指定一个不同于process
类型参数的子类型。我不明白的是我应该如何反击这样的情况。
我知道有这样的设计问题的标签,但我不记得它的名字。如果有人可以加入,那将是值得赞赏的。如果您觉得我遗漏了相关信息,请告诉我
答案 0 :(得分:1)
interface State<T extends Script> {
}
在T
的子类被实例化之前,我们无法确定State
将是什么。我们知道它将是Script
的子类型,但我们无法确定哪一个。
由于我们没有实例化TransitionTable
中的任何状态,因此我们不知道哪个Script
子类型被用于传递给TransitionTable
的状态。由于我们不确定哪个子类型用于状态,因此我们无法确定<T extends Script>
是否为正确的子类型。
为了解决这个问题,我让TransitionTable
接受了泛型类型的参数;国家的类型。我将StateNode
从静态嵌套类更改为常规嵌套类,因此它继承了泛型类型:
public class TransitionTable<T extends Script<?>> {
private Map<T, StateNode> entries = new HashMap<>()
private StateNode current, start;
public T getCurrentState() {
return current.state;
}
public StateNode createNode(T state) {
StateNode entry = new StateNode(state);
entries.put(state, entry);
if (startNode == null) {
startNode = currentNode = entry;
}
return entry;
}
public class StateNode {
private ArrayList<T> nodes = new ArrayList<>();
private T state;
private StateNode(T state) {
this.state = state;
}
public StateNode addTransition(T state) {
nodes.add(state);
return this;
}
}
}
然后将处理带到我声明所有类型的地方:
State<DemoScript> start = new StartState();
State<DemoScript> walkToA = new WalkToAState();
State<DemoScript> walkToB = new WalkToBState();
State<DemoScript> dance = new DanceState();
TransitionTable<State<DemoScript>> table = new TransitionTable();
table.createNode(start).addTransition(walkToA).addTransition(walkToB);
table.createNode(dance).addTransition(walkToA).addTransition(walkToB);
table.createNode(walkToA).addTransition(dance);
table.createNode(walkToB).addTransition(dance);
public void loop() {
table.getCurrentState().process(this, table);
}
不要评论它的详细程度;我将来会改进它
答案 1 :(得分:0)
不知何故,如果您正在尝试处理的脚本可以被当前状态接受,那么您将不得不进行检查,并且我没有看到(简单)执行此编译时的方法。在运行时执行此操作的一种方法是在您的州中存储Class<T>
引用并调用Class.isAssignableFrom
。
另一种方法是让调用代码自己解决问题。您可以通过从process
中移除TransitionTable
方法并将其替换为以下内容来执行此操作:
public <T extends Script> Processor<T> getProcessorForCurrentState() {
//This cast can't go wrong because we haven't bound T yet
State<T> current = (State<T>)currentNode.mainState;
return new Processor<T>(current);
}
public static class Processor<T extends Script> {
private State<T> state;
private Processor(State<T> state) {
this.state = state;
}
public void process(T script) {
//TODO make sure this isn't called twice
this.state.process(script);
}
}
但同样,这会将运行时检查中继到客户端代码而不是表中。这取决于您的情况,如果这是可取的。