Методи Java Enum - повернення enum у зворотному напрямку


113

Я хотів би оголосити Enum 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 випадки.
Sotirios Delimanolis

12
До речі, d.getCode() * -1==-d.getCode()
tckmn

Глава 6 (принаймні у 2E) ефективної Java від Bloch може бути цікавою і настійно рекомендується.
jedwards

ntu.edu.sg/home/ehchua/programming/java/javaenum.html , розділ 2.1 чітко пояснив поняття
vikramvi

Відповіді:


206

Для тих, хто заманюється сюди за назвою: так, ви можете визначити свої власні методи у перерахунку. Якщо вам цікаво, як викликати такий нестатичний метод, ви робите це так само, як і будь-який інший нестатичний метод - ви викликаєте його на зразок типу, який визначає або успадковує цей метод. У разі перерахунків такі випадки просто ENUM_CONSTANTs.

Отже, все, що вам потрібно, це 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]; 
    }
}

3
Я збирався .values()[ordinal()]зламати хаку, але такий підхід набагато надійніший
jedwards

як ти цим користуєшся? і як називається ця техніка?
Туфір

1
@Thufir " як ти ним користуєшся ", якщо ти запитуєш про метод, то як і будь-який інший метод - ти за допомогою цього методу викликаєш його в екземплярі класу. Примірники Directionкласу перерахувань NORTH, EAST, SOUTH, WESTтак що ви можете просто використовувати , NORTH.getOppositeDirection()і він повернеться SOUTH. " як називається ця техніка? ", якщо ви запитуєте про static{...}це, то це блок статичної ініціалізації , він викликається кодом при першому завантаженні класу (це частина того ж процесу, який ініціалізує статичні поля).
Пшемо

@Pshemo, мені цікаво, як буде весняна версія вищевказаного коду, якщо значення, які встановлюються в статичний блок, потрібно вводити з файла властивостей say.
Вікас Прасад

162

Для такої невеликої кількості переживань я вважаю найбільш читаним рішення:

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

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, може бути краще вказати, що помилка програміста не була визначена протилежна для нещодавно доданого напрямку.
Тимофій055,

1
Це вимагає, щоб абонент обробляв нуль. Він також вимагає від технічного обслуговування, щоб вони додавали регістр кожного разу, коли додається новий тип напряму. Дивіться відповідь Аміра Афгані про використання абстрактного методу, який може бути замінений кожним значенням enum, таким чином, ви ніколи не ризикуєте пропустити його, і вам не доведеться турбуватися про обробку нуля.
Майкл Петерсон

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();
}

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;
 }

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

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

Енуми мають метод статичних значень, який повертає масив, що містить усі значення перерахунку в порядку, в якому вони оголошені. джерело

оскільки північ отримує 1, СХІД отримує 2, ПІВДЕНЬ отримує 3, ЗАХІД отримує 4; ви можете створити просте рівняння, щоб отримати протилежне:

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


2
чому це відповідь? Як ви очікуєте, що це допоможе майбутнім читачам чи комусь із цього питання навчитися без будь-яких пояснень?
GrumpyCrouton

Хоча цей код може відповісти на питання, надаючи додатковий контекст щодо того, як та / або чому він вирішує проблему, покращить довгострокове значення відповіді. Пам’ятайте, що ви відповідаєте на запитання читачів у майбутньому, а не лише про людину, яка зараз запитує! Будь ласка, відредагуйте свою відповідь, щоб додати пояснення та вказати, які обмеження та припущення застосовуються. Також не завадить згадати, чому ця відповідь доречніша за інші.
ItamarG3

важко читати код без коментарів? або вам потрібен ексклюзивний javadoc для 7 рядків коду?
Прегунтон

1
Це рішення є крихким, оскільки залежить від впорядкування значень перерахувань. Якби ви змінили порядок на алфавіт, ваше розумне рівняння більше не забезпечить правильну протилежність.
Джош J
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.