У меня есть интерфейс как следующее:
package example;
import java.awt.Point;
public interface Thing {
public enum MovingState {
MOVING_LEFT,
MOVING_UP,
MOVING_RIGHT,
MOVING_DOWN
}
public void setNewPosition(MovingState state);
public Point getPosition();
}
и класс реализации:
package example;
import java.awt.Point;
public class ThingImpl implements Thing {
public enum MovingState {
MOVING_LEFT (-1, 0),
MOVING_UP (0, -1),
MOVING_RIGHT (1, 0),
MOVING_DOWN (0, 1);
private int x_move;
private int y_move;
MovingState(int x, int y) {
x_move = x;
y_move = y;
}
public int xMove() {
return x_move;
}
public int yMove() {
return y_move;
}
}
private Point position;
public void setNewPosition(MovingState state) {
position.translate(state.xMove(), state.yMove());
}
public Point getPosition() {
return position;
}
}
Идея состоит в том, чтобы иметь MovingState
в ThingImpl
расшириться MovingState
от Thing
интерфейс (таким образом разделение фактической реализации MovingState
от интерфейса).
Это не работает хотя - MovingState
перечисление в ThingImpl
тени определение в интерфейсе вместо того, чтобы расширить его, затем компилятор жалуется, что ThingImpl не абстрактен и не переопределяет абстрактный метод setNewPosition (Вещь. MovingState) в Вещи.
Существует ли фактический способ сделать то, чего я пытаюсь достигнуть? Или разве Java просто не имеет этой возможности?
Что вы действительно хотите сделать, так это удалить объявление перечисления из вашего класса "ThingImpl" и перенести все это (включая конструктор и геттеры) в интерфейс Thing.
Сделайте ваши поля конечными в перечислении, чтобы помнить, что их нельзя трогать.
Таким образом, все, что хочет использовать интерфейс Thing, должно использовать перечисление, определенное в вашем интерфейсе - ваша проблема в том, что вы фактически определяете его дважды, но оно должно быть либо в интерфейсе (что хорошо, если оно будет использоваться только для этого интерфейса), либо как Java-файл перечисления публичного уровня (используя public enum вместо public class). Вы сделаете его публичным перечислением, если что-то, кроме вашего интерфейса, может разумно ожидать его использования - Map.Entry, на мой взгляд, является плохим вложенным интерфейсом, потому что другие классы используют пару ключ/значение, внешнюю по отношению к карте, и поэтому он должен быть собственным интерфейсом, но мы должны жить с этим :(
Идея состоит в том, чтобы MovingState в ThingImpl расширял MovingState из интерфейса Thing (таким образом отделяя фактическую реализацию MovingState от интерфейса).
Я не думаю, что это действительно ваша идея - я думаю, что поведение, которое вы указали для интерфейса Thing, прекрасно, вы действительно не хотите трогать перечисление MovingState, поскольку оно прекрасно и так. Однако если вы считаете, что для чего-то нужна другая реализация MovingState, вы можете сделать так, чтобы она реализовывала интерфейс под названием MovingState, и таким образом вы можете переименовать ваше перечисление в DefaultMovingState. Выбор за вами.
Ваш интерфейс MovingState будет просто иметь геттеры, которые вы сейчас раскрываете в MovingState. Два метода.
polygenelubricants дала вам свой ответ. Я хотел бы добавить к нему кое-что, потому что это была проблема, с которой я столкнулся.
Допустим, у вас есть интерфейс, который имеет множество реализаций. Один метод в интерфейсе принимает в качестве аргумента перечисление
. В идеале вы хотели бы иметь определенные наборы пронумерованных значений для каждой реализации. Однако, поскольку вы не можете расширять перечисления, вы не можете создавать «базовые» перечисления и расширять их. Наивный подход заключался бы в наличии «божественного» перечисления, которое поддерживает полный набор перечисляемых значений. Но есть способ получше. Вы можете использовать так называемый интерфейс маркера . Эффективная Java 2-е изд. говорит и об этом. Интерфейс маркера не будет содержать никаких объявлений методов, а просто помечает класс как определенный тип.
Таким образом, вы можете определить интерфейс маркера, и все ваши перечисления будут реализовывать этот интерфейс. Это связано с тем, чтобы сделать enum
расширяемым, потому что, если вы определяете интерфейс, который расширяет ваше enum
s, вы автоматически помечаете свои enum
s как определенные тип.
Таким образом, вы можете сохранить все конкретные перечисленные значения вместе с их конкретными реализациями (разделение проблем).
В вашем случае вы можете сделать что-то вроде этого:
public interface MovingInterface {
int xMove();
int yMove();
}
а затем:
public enum MovingState implements MovingInterface {
MOVING_LEFT (-1, 0),
MOVING_UP (0, -1),
MOVING_RIGHT (1, 0),
MOVING_DOWN (0, 1);
private int x_move;
private int y_move;
MovingState(int x, int y) {
x_move = x;
y_move = y;
}
public int xMove() {
return x_move;
}
public int yMove() {
return y_move;
}
}
Вы не можете расширить перечисление
, потому что оно final
.
Вы можете прочитать Эффективное второе издание Java, пункт 34: Эмуляция расширяемых перечислений с интерфейсами . По сути, это сводится к самому перечислению
, которое реализует Something
.
То, что вы пытаетесь сделать, является антипаттерном. Константы не должны определяться в интерфейсе.
http://en.wikipedia.org/wiki/Constant_interface
Я бы переместил константы из вашего интерфейса или просто сделал их методами, и позволил вашим реализациям определять их, реализуя эти методы ...