问题
I am currently struggling with a java abstraction problem. I have something like this:
public interface State {
};
public interface Dynamics {
getObservationChance(State state, Observation observation);
};
class SpecialState implements State {
};
enum SpecialObservation() {
FREE, WALL, etc.
}
class SpecialDynamics implements Dynamics {
getObservationChance(State state, Observation observation) {
// state should be SpecialState, observation should be SpecialObservation!
}
};
class Main {
Main(State state, Observation observation, Dynamics dynamics) {
dynamics.getObservationChance(state, observation);
}
};
SecialObservation should be an enum (or something like that) of possible observations, but I want to have an abstract representation of the problem. So I want an Observation that should contain the observation and a function that returns a list of all possible observations. The last thing is very important for an algorithm I am implementing, since I have to summarize over all possible observations.
Thanks!
回答1:
You need parametrized types here - you have families of tree types each: a state, a observation, and a dynamics.
If we take the observation enum as the parameter type, we could convert your type to something like this:
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);
}
}
This approach only works if the methods of our State interface are enough for the getObservationChance method, of course.
A more general approach would be to parametrize over all three types:
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);
}
Then we can define the implementations as this:
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!
}
}
The main class then needs all three parameters, of course:
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);
}
}
In your case in fact the dynamics is only dependent on the observation and state, and not the other way around (and these are not dependent on each other), so another way would be this:
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);
}
}
Edit:
About the getAllObservations method:
As long as you somehow can make your type parameters concrete, there is no real problem here.
To access the list of enum constants of a specific type, you need access to this type - either directly (SpecialObservation.values()
), or with a class object like here:
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);
}
}
}
(This only works if O is an enum class, of course.)
If you have a mixed list, it gets more complicated, and then it is also not really easy to type-safely match the Dynamics, Action, Observation and State classes.
回答2:
You could add methods to your enums:
enum SpecialObservation() implements Observation{
FREE{
void doSth(){
}
Collection<Observation> getPossibleObservations{
}
}, WALL, etc.
}
回答3:
Use an interface:
interface Observation{
Collection<Observation>getSubObservations();
}
enum SpecialObservation implements Observation {
FREE,
DOOR (FREE),
WINDOW (FREE),
WALL (DOOR, WINDOW, FREE);
private Collection<Observation> subObservations;
private SpecialObservation(Observation... subObservations) {
this.subObservations = subObservations;
}
public Collection<Observation> getSubObservations() {
return subObservations;
}
}
回答4:
Because of the way Java implements enums, it is not possible to have abstract enums, and with correct design you will not feeel the need for one. However, so far as I remember, you can define your own methods within enum...(you may have to check this..I've not been in touch with Java for some time now)
来源:https://stackoverflow.com/questions/5539064/enum-abstraction-problem