Java: интерфейсы, содержащие внутренние перечисления; Расширение функциональности в классах реализации - PullRequest
9 голосов
/ 14 июля 2010

У меня есть такой интерфейс:

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 ( Thing.MovingState) в Thing.

Есть ли реальный способ сделать то, чего я пытаюсь достичь? Или у Java просто нет этой возможности?

Ответы [ 4 ]

11 голосов
/ 14 июля 2010

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

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

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

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

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

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

5 голосов
/ 14 июля 2010

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

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

3 голосов
/ 14 июля 2010

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

Допустим, у вас есть интерфейс, который имеет много реализаций.Один метод в интерфейсе принимает enum в качестве аргумента.В идеале вы хотели бы иметь конкретные наборы перечисляемых значений для реализации.Однако, поскольку вы не можете расширять перечисления, вы не можете создавать «базовые» перечисления и расширять их.Наивным подходом было бы иметь перечисление «бога», которое поддерживает полный набор перечисляемых значений.Но есть и лучший способ.Вы можете использовать так называемый маркерный интерфейс .Эффективная Java 2-е изд.об этом тоже говорит. интерфейс маркера не будет содержать никаких объявлений метода, а просто помечает класс как определенный тип.

Таким образом, вы можете определить интерфейс маркера и иметь все своиперечисления реализуют этот интерфейс.Это связано с созданием расширяемого enum, потому что если вы определяете интерфейс, который расширяют ваши enum, вы автоматически помечаете свои 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;
    }
}
0 голосов
/ 14 июля 2010

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...