Методы Java Enum - вернуть перечисление в противоположном направлении

113

Я хотел бы объявить перечисление Direction, которое имеет метод, возвращающий противоположное направление (следующее синтаксически неверно, т. Е. Перечисления не могут быть созданы, но это иллюстрирует мою точку зрения). Возможно ли это в Java?

Вот код:

public enum Direction {

     NORTH(1),
     SOUTH(-1),
     EAST(-2),
     WEST(2);

     Direction(int code){
          this.code=code;
     }
     protected int code;
     public int getCode() {
           return this.code;
     }
     static Direction getOppositeDirection(Direction d){
           return new Direction(d.getCode() * -1);
     }
}
Скайлер
источник
Просто используйте переключатель. У вас всего 4 случая.
Сотириос Делиманолис
12
Кстати, d.getCode() * -1==-d.getCode()
tckmn
Глава 6 (по крайней мере, в 2E) Эффективной Java Блоха может быть интересна и настоятельно рекомендуется.
jedwards
ntu.edu.sg/home/ehchua/programming/java/javaenum.html , раздел 2.1 объяснил концепцию
vikramvi

Ответы:

207

Для тех, кого соблазнил здесь заголовок: да, вы можете определить свои собственные методы в своем перечислении. Если вам интересно, как вызвать такой нестатический метод, вы делаете это так же, как и с любым другим нестатическим методом - вы вызываете его для экземпляра типа, который определяет или наследует этот метод. В случае перечислений такие экземпляры простоENUM_CONSTANT s.

Так что все, что вам нужно, это EnumType.ENUM_CONSTANT.methodName(arguments).


Теперь вернемся к проблеме из вопроса. Одним из решений может быть

public enum Direction {

    NORTH, SOUTH, EAST, WEST;

    private Direction opposite;

    static {
        NORTH.opposite = SOUTH;
        SOUTH.opposite = NORTH;
        EAST.opposite = WEST;
        WEST.opposite = EAST;
    }

    public Direction getOppositeDirection() {
        return opposite;
    }

}

Сейчас Direction.NORTH.getOppositeDirection()вернусь Direction.SOUTH.


Вот немного более «хитрый» способ проиллюстрировать комментарий @jedwards, но он не кажется таким гибким, как первый подход, поскольку добавление дополнительных полей или изменение их порядка нарушит наш код.

public enum Direction {
    NORTH, EAST, SOUTH, WEST;

    // cached values to avoid recreating such array each time method is called
    private static final Direction[] VALUES = values();

    public Direction getOppositeDirection() {
        return VALUES[(ordinal() + 2) % 4]; 
    }
}
Pshemo
источник
3
Я собирался создать .values()[ordinal()]хакер, но этот подход намного надежнее
jedwards
а как вы его используете? и как называется эта техника?
Thufir 07
1
@Thufir « как вы его используете », если вы спрашиваете о методе, то, как и любой другой метод, вы вызываете его в экземпляре класса с этим методом. Экземпляры Directionкласса перечислений NORTH, EAST, SOUTH, WESTтак что вы можете просто использовать , NORTH.getOppositeDirection()и он вернется SOUTH. «Как называется эта техника? » - если вы спрашиваете, static{...}то это статический блок инициализации , это код, вызываемый при первой загрузке класса (это часть того же процесса, который инициализирует статические поля).
Pshemo 07
@Pshemo, мне интересно, как будет выглядеть Spring версия вышеприведенного кода, если значения, которые устанавливаются в статическом блоке, должны быть введены из, скажем, файла свойств.
Викас Прасад
161

Для такого небольшого перечисления я считаю наиболее удобочитаемым решением:

public enum Direction {

    NORTH {
        @Override
        public Direction getOppositeDirection() {
            return SOUTH;
        }
    }, 
    SOUTH {
        @Override
        public Direction getOppositeDirection() {
            return NORTH;
        }
    },
    EAST {
        @Override
        public Direction getOppositeDirection() {
            return WEST;
        }
    },
    WEST {
        @Override
        public Direction getOppositeDirection() {
            return EAST;
        }
    };


    public abstract Direction getOppositeDirection();

}
Амир Афгани
источник
8
Отличная идея! Это также хорошо, когда вы обычно хотите, чтобы каждое значение перечисления имело определенное поведение.
OferBr 09
28

Это работает:

public enum Direction {
    NORTH, SOUTH, EAST, WEST;

    public Direction oppose() {
        switch(this) {
            case NORTH: return SOUTH;
            case SOUTH: return NORTH;
            case EAST:  return WEST;
            case WEST:  return EAST;
        }
        throw new RuntimeException("Case not implemented");
    }
}
Пикрасс
источник
8
Вместо того, чтобы возвращать значение null, предложение по умолчанию, которое генерирует подходящее исключение RuntimeException, может быть лучше, чтобы указать, что произошла ошибка программиста в том, что не было определено противоположное для вновь добавленного направления.
Timothy055
1
Это требует, чтобы вызывающий обработал значение null. Также от сопровождающего требуется, чтобы он добавлял кейс каждый раз, когда добавляется новый тип направления. См. Ответ Амира Афгани об использовании абстрактного метода, который может быть переопределен каждым значением перечисления, таким образом вы никогда не рискуете пропустить одно и вам не придется беспокоиться об обработке null.
Майкл Петерсон,
14

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

Однако он не очень хорошо читается; возможно, switchбыло бы более управляемым?

public enum Direction {
    NORTH(1) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.SOUTH;
        }
    },
    SOUTH(-1) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.NORTH;
        }
    },
    EAST(-2) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.WEST;
        }
    },
    WEST(2) {
        @Override
        public Direction getOppositeDirection() {
            return Direction.EAST;
        }
    };

    Direction(int code){
        this.code=code;
    }
    protected int code;

    public int getCode() {
        return this.code;
    }

    public abstract Direction getOppositeDirection();
}
Makoto
источник
4

Да, мы делаем это постоянно. Вы возвращаете статический экземпляр, а не новый объект

 static Direction getOppositeDirection(Direction d){
       Direction result = null;
       if (d != null){
           int newCode = -d.getCode();
           for (Direction direction : Direction.values()){
               if (d.getCode() == newCode){
                   result = direction;
               }
           }
       }
       return result;
 }
BevynQ
источник
0
public enum Direction {
    NORTH, EAST, SOUTH, WEST;

    public Direction getOppositeDirection(){
        return Direction.values()[(this.ordinal() + 2) % 4];
    }
}

Перечисления имеют метод статических значений, который возвращает массив, содержащий все значения перечисления в порядке их объявления. источник

так как СЕВЕР получает 1, ВОСТОК получает 2, ЮГ получает 3, ЗАПАД получает 4; вы можете составить простое уравнение, чтобы получить противоположное:

(значение + 2)% 4

Pregunton
источник
2
почему это ответ? Как вы ожидаете, что это поможет будущим читателям или кому-то еще, учиться без каких-либо объяснений?
GrumpyCrouton
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, как и / или почему он решает проблему, улучшит долгосрочную ценность ответа. Помните, что вы отвечаете на вопрос для будущих читателей, а не только для человека, который задает его сейчас! Отредактируйте свой ответ, чтобы добавить пояснение и указать, какие ограничения и предположения применяются. Также не помешает упомянуть, почему этот ответ более уместен, чем другие.
ItamarG3
сложно читать код без комментариев? или вам нужен эксклюзивный javadoc для 7 строк кода?
Pregunton
1
Это решение хрупкое, поскольку оно зависит от порядка значений перечисления. Если бы вы изменили порядок на алфавитный, ваше умное уравнение больше не дало бы правильной противоположности.
Джош Дж