正确使用泛化

时间:2014-10-15 08:52:09

标签: java oop

假设您有以下接口

public interface Action {
    public State execute(State state);  
}
public interface State {
    public Collection<Action> getPossibleActions(); 
}

这个方法

public static Collection<State> getAllSuccessorStates(State state){
    Collection<State> allSuccessors = new HashSet<>();
    for (Action action: state.getPossibleActions()){
        State successorState = action.execute(state);
        allSuccessors.add(successorState);
        allSuccessors.addAll(getAllSuccessorStates(successorState));
    }
    return allSuccessors;
}

一个具体的国家可以是一个棋盘,一个动作就是棋子在棋盘上的运动。显然,Chess-Actions需要知道具体的State类:

public class ChessAction implements Action {
    @Override
    public ChessState execute(ChessState state) {...}
}

当然,这不是覆盖执行的允许方式。实现这个的正确方法是什么,所以你可以拥有对具体状态进行操作的具体动作,你可以将它们作为getAllSuccessorStates的参数给出?

我想到了Generics并且也得到了指向Generics的答案,但这带来了新的问题。如果我像这样编写Action类:

public interface Action<E extends State> {
    public E execute(E state);
}

我将在ChessState类中遇到以下问题:

@Override
public Collection<Action<State>> getPossibleActions() {
    Collection<Action<State>> actions = new ArrayList<>();
    actions.add(new ChessAction());
    return actions;
}

行Actions.add导致以下错误:类型Collection&gt;中的方法add(Action)不适用于参数(ChessAction)
现在我可以宣布行动为

Collection<Action<ChessState>> actions = new ArrayList<>(); 

但不是允许的返回类型。

4 个答案:

答案 0 :(得分:3)

您可以使用泛型(需要java 1.5或更高版本):

public interface Action<T extends State> {
    public T execute(T state);  
}

public class ChessAction implements Action<ChessState> {
    @Override
    public ChessState execute(ChessState state) {...}
}

希望有所帮助。

答案 1 :(得分:1)

我现在找到了一个令人满意的解决方案,它正常工作,不需要instanceof并且不会产生编译警告:

public interface Action<E extends State<?>> {
    public E execute(E state);  
}
public interface State<E extends Action<?>> {
    public Collection<E> getPossibleActions();
}
public static <A extends Action<S>, S extends State<A>> Collection<S> getAllSuccessorStates(S state){
    Collection<S> allSuccessors = new HashSet<>();
    for (A localAction: state.getPossibleActions()){
        S successorState = localAction.execute(state);
        allSuccessors.add(successorState);
        allSuccessors.addAll(getAllSuccessorStates(successorState));
    }
    return allSuccessors;
}

使用getAllSuccessorStates的示例(我放弃了具体类的实现细节,但Point应该是显而易见的。您可以将getAllSuccessorStates方法与任何具体的State类一起使用,在返回集合中获取此类的实例并使用它们)

public class TestState implements State<TestAction> {...}
public class TestAction implements Action<TestState> {...}
public static void main(String[] args) {
    TestState initialState = new TestState("1");
    Collection<TestState> allSuccessorStates = getAllSuccessorStates(initialState);
    for (TestState state: allSuccessorStates){
        System.out.println(state.getStateStr());
    }
}

这个问题起源于这本书&#34; AI-A现代的方法&#34;作者:Stuart Russel和Peter Norvig,如果读这本书的人有同样的问题并寻找解决方案。在书中,Action和State方法都在一个问题类中,但我认为这样OO设计更好。

答案 2 :(得分:0)

好吧,ChessState必须与execute接口中的Action签名相同。如果您要求ChessAction.execute仅接受ChessState,则可以写:

public class ChessAction implements Action {
    @Override
    public State execute(State state) 
    {
        if (!(state instanceof ChessState))
            throw new SomeException ();
        ChessState cs = (ChessState) state;
        ...
    }
}

答案 3 :(得分:0)

你需要实现相同的继承方法,它将起作用,因为State是CheesState的超类。

public class ChessAction implements Action {
@Override
public State execute(State state) {...}
}

在execute方法中,你可以使用polymorph方法(在State中定义并重新定义ChessState),或者你可以转换为ChessState(ChessState s =(ChessState)状态;),然后根据需要使用它