Java: Интерфейсы, содержащие внутренние перечисления; Расширение функциональности в классах реализации

У меня есть интерфейс как следующее:

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 просто не имеет этой возможности?

9
задан MatthewD 13 July 2010 в 22:44
поделиться

4 ответа

Что вы действительно хотите сделать, так это удалить объявление перечисления из вашего класса "ThingImpl" и перенести все это (включая конструктор и геттеры) в интерфейс Thing.

Сделайте ваши поля конечными в перечислении, чтобы помнить, что их нельзя трогать.

Таким образом, все, что хочет использовать интерфейс Thing, должно использовать перечисление, определенное в вашем интерфейсе - ваша проблема в том, что вы фактически определяете его дважды, но оно должно быть либо в интерфейсе (что хорошо, если оно будет использоваться только для этого интерфейса), либо как Java-файл перечисления публичного уровня (используя public enum вместо public class). Вы сделаете его публичным перечислением, если что-то, кроме вашего интерфейса, может разумно ожидать его использования - Map.Entry, на мой взгляд, является плохим вложенным интерфейсом, потому что другие классы используют пару ключ/значение, внешнюю по отношению к карте, и поэтому он должен быть собственным интерфейсом, но мы должны жить с этим :(

Идея состоит в том, чтобы MovingState в ThingImpl расширял MovingState из интерфейса Thing (таким образом отделяя фактическую реализацию MovingState от интерфейса).

Я не думаю, что это действительно ваша идея - я думаю, что поведение, которое вы указали для интерфейса Thing, прекрасно, вы действительно не хотите трогать перечисление MovingState, поскольку оно прекрасно и так. Однако если вы считаете, что для чего-то нужна другая реализация MovingState, вы можете сделать так, чтобы она реализовывала интерфейс под названием MovingState, и таким образом вы можете переименовать ваше перечисление в DefaultMovingState. Выбор за вами.

Ваш интерфейс MovingState будет просто иметь геттеры, которые вы сейчас раскрываете в MovingState. Два метода.

11
ответ дан 4 December 2019 в 12:16
поделиться

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;
    }
}
3
ответ дан 4 December 2019 в 12:16
поделиться

Вы не можете расширить перечисление , потому что оно final .

Вы можете прочитать Эффективное второе издание Java, пункт 34: Эмуляция расширяемых перечислений с интерфейсами . По сути, это сводится к самому перечислению , которое реализует Something .

5
ответ дан 4 December 2019 в 12:16
поделиться

То, что вы пытаетесь сделать, является антипаттерном. Константы не должны определяться в интерфейсе.

http://en.wikipedia.org/wiki/Constant_interface

Я бы переместил константы из вашего интерфейса или просто сделал их методами, и позволил вашим реализациям определять их, реализуя эти методы ...

-2
ответ дан 4 December 2019 в 12:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: