不能使用在方法级别声明的泛型类型的方法参数,该方法参数需要在其他地方声明的泛型类型

时间:2014-12-30 04:55:43

标签: java generics

我的最终目标是拥有一个StateTransitionTable,客户端可以在其中创建一个由mainState组成的条目,并声明它可以从该主要状态跳转到({1}中存储})。此应用程序ArrayList在循环上运行,转换表应该在某种程度上自我操作。

请不要建议我使用enum;它们不能很好地扩展

每个州都应该有权访问ScriptScript个实例。该脚本用于执行它的任务,该表将使用索引转换到下一个状态。索引取决于转换添加到条目的顺序。创建的第一个条目是第一个使用的条目。

在应用程序的循环中,应该访问存储在当前条目中的状态,并且应该从中调用TransitionTableprocess调用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类型参数的子类型。我不明白的是我应该如何反击这样的情况。

我知道有这样的设计问题的标签,但我不记得它的名字。如果有人可以加入,那将是值得赞赏的。如果您觉得我遗漏了相关信息,请告诉我

2 个答案:

答案 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);
    }
}

但同样,这会将运行时检查中继到客户端代码而不是表中。这取决于您的情况,如果这是可取的。