Здесь вам нужны параметризованные типы - у вас есть семейства типов деревьев: состояние, наблюдение и динамика.
Если мы берем перечисление наблюдения в качестве типа параметра, мы можем преобразовать ваш тип вчто-то вроде этого:
public interface Observation<O extends Observation<O>> {
...
}
public interface State<O extends Observation<O>> {
}
public interface Dynamics<O extends Observation<O>> {
getObservationChance(State<O> state, O observation);
}
enum SpecialObservation implements Observation<SpecialObservation> {
FREE, WALL, etc.
}
class SpecialState implements State<SpecialObservation> {
}
class SpecialDynamics implements Dynamics<SpecialObservation> {
getObservationChance(State<SpecialObservation> state, SpecialObservation observation) {
// state should be SpecialState, observation should be SpecialObservation!
}
}
class Main<O extends Observation> {
Main(State<O> state, O observation, Dynamics<O> dynamics) {
dynamics.getObservationChance(state, observation);
}
}
Этот подход работает, только если методов нашего интерфейса State достаточно для метода getObservationChance, конечно.
Более общий подход заключается в параметризации по всемтри типа:
public interface Observation<O extends Observation<O, S, D>,
S extends State<O,S,D>,
D extends Dynamics<O,S,D>>
{
...
}
public interface State<O extends Observation<O,S,D>,
S extends State<O,S,D>,
D extends Dynamics<O,S,D>> {
}
public interface Dynamics<O extends Observation<O,S,D>,
S extends State<O,S,D>,
D extends Dynamics<O,S,D>> {
getObservationChance(S state, O observation);
}
Затем мы можем определить реализации следующим образом:
enum SpecialObservation implements Observation<SpecialObservation, SpecialState, SpecialDynamics> {
FREE, WALL, etc.
}
class SpecialState implements State<SpecialObservation, SpecialState, SpecialDynamics> {
}
class SpecialDynamics implements Dynamics<SpecialObservation, SpecialState, SpecialDynamics> {
getObservationChance(SpecialObservation state, SpecialObservation observation) {
// state should be SpecialState, observation should be SpecialObservation!
}
}
Тогда главному классу нужны все три параметра, конечно:
class Main<O extends Observation<O,S,D>,
S extends State<O,S,D>,
D extends Dynamics<O,S,D>> {
Main(S state, O observation, D dynamics) {
dynamics.getObservationChance(state, observation);
}
}
В вашем случае на самом деле динамика зависит только от наблюдения и состояния, а не наоборот (и они не зависят друг от друга), так что другой способ будет таким:
public interface Observation {
...
}
public interface State {
}
public interface Dynamics<S extends State,
O extends Observation> {
getObservationChance(S state, O observation);
}
enum SpecialObservation implements Observation {
FREE, WALL, etc.
}
class SpecialState implements State {
}
class SpecialDynamics implements Dynamics<SpecialState, SpecialObservation> {
getObservationChance(SpecialState state, SpecialObservation observation) {
// state should be SpecialState, observation should be SpecialObservation!
}
}
class Main<S extends State, O extends Observation> {
Main(S state, O observation, Dynamics<S, O> dynamics) {
dynamics.getObservationChance(state, observation);
}
}
Редактировать: О методе getAllObservations: Пока вы каким-то образом можете конкретизировать параметры вашего типа, здесь нет никаких реальных проблем.Чтобы получить доступ к списку констант enum определенного типа, вам нужен доступ к этому типу - либо напрямую (SpecialObservation.values()
), либо с объектом класса, как здесь:
class Main<S extends State, O extends Observation> {
public O[] getAllObservations(Class<O> oClass) {
return oClass.getEnumConstants();
}
Main(S state, Dynamics<S, O> dynamics, Class<O> observationClass) {
O[] observations = getAllObservations(observationClass);
for(O o : observations) {
dynamics.getObservationChance(state, observation);
}
}
}
(Это работает только если Oконечно же, является перечислимым классом.)
Если у вас смешанный список, он усложняется, а затем не совсем легко выполнить безопасное сопоставление типов с классами Dynamics, Action, Observation и State.