Используйте перечисления для индексов массива в Java - PullRequest
32 голосов
/ 24 марта 2011

Читая «Эффективную Java», я наткнулся на предложение «использовать перечисления вместо int констант». В текущем проекте я делаю что-то похожее на это ниже:

int COL_NAME = 0;
int COL_SURNAME = 1;

table[COL_NAME] = "JONES" 

Как бы я использовал перечисления вместо этого? Из-за интерфейса, который я вынужден использовать, я должен использовать int для своего индекса. Приведенный выше пример является лишь примером. Я на самом деле использую API, который принимает int для значений индекса.

Ответы [ 7 ]

50 голосов
/ 24 марта 2011

Применение одного полезного шаблона вместе с анти-шаблоном часто дает сбой; -)

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

Чистое (er) решение было бы что-то вроде EnumMap со значениями enum в качестве ключей.

В качестве альтернативы вы можете использовать table[COL_NAME.ordinal()], если вам абсолютно необходимо.

Если какой-либо API вынуждает вас передавать значения int, но вы имеете контроль над фактическими значениями (то есть вы можете передавать свои собственные константы), тогда вы можете переключиться на использование значений enum в своем коде и преобразовать в / с enum только в тех местах, где ваш код взаимодействует с API. Обратная операция enumValue.ordinal() - EnumClass.values()[ordinal]).

6 голосов
/ 24 марта 2011

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

enum Column {
   NAME, SURNAME
}

Map<Column, String> table = new EnumMap<Column, String>(Column.class);

table.put(Column.NAME, "JONES");

String name = table.get(Column.NAME);

Это было бы намного проще, если бы вы использовали POJO.

classPerson {
   String name;
   String surname;
}

Person person = new Person();
person.name = "JONES";
String name = person.name;
1 голос
/ 16 августа 2012

В зависимости от значений вашего массива, вы можете вместо этого смотреть на объект. Например, если ваш массив выглядит примерно так

person[FIRST_NAME] = "Jim Bob"
person[SURNAME] = "Jones"
person[ADDRESS] = "123 ABC St"
person[CITY] = "Pleasantville"
...

Тогда вы действительно хотите что-то вроде этого

Person jimBob = new Person("Jim Bob", "Jones");
jimBob.setAddress("123 ABC St", "Pleasantville", "SC");
....

См. Рефакторинг: заменить массив на объект

1 голос
/ 24 марта 2011

Поскольку вы должны использовать эту таблицу и, следовательно, не можете использовать EnumMap, я лично считаю, что лучшим решением будет придерживаться того, что у вас есть.В перечислении порядковые значения элементов не должны иметь никакого внутреннего значения, в то время как в вашем случае они имеют, поскольку они используются в качестве индексов в таблице.

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

Теперь вы можете решить ее, обернув таблицу в свой собственный класс, который обращается к ней,и использовать перечисления в и из этого класса.Но это вводит больше кода, еще один уровень косвенности и фактически не решает никаких проблем для вас, за исключением того, что enum немного легче поддерживать, чем список значений int (значительно компенсируется необходимостью поддерживать оболочку, которую вы сейчас написали).

Вы могли бы рассмотреть эту работу, если вы пишете общедоступный API, который будут использовать другие люди, поскольку это позволит избежать зависимости их от некоторых магических значений, которые могут со временем меняться (тесная связь, которая может сломать вещи).Если да, то скорее всего вам подходит оболочка, которая использует EnumMap внутри.

В противном случае оставьте все как есть.

1 голос
/ 24 марта 2011

Зависит от требований. Вы можете использовать Enum.ordinal() для преобразования enum в int.

Обратите внимание, что невозможно передать Enum непосредственно в качестве индекса.

Edit:

Другой возможностью было бы использовать Map<YourEnum, String> map, а затем использовать map.get(EnumValue).

0 голосов
/ 24 ноября 2014

Вы можете определить enum с помощью конструктора следующим образом:

public enum ArrayIndex {
    COL_NAME(0), COL_SURNAME(1);
    private int index;

    private ArrayIndex(int index) {
    this.index = index;
    }

public int getIndex() {
    return this.index;
}
};   

И затем использовать его следующим образом:

public static void main (String args[]) {
    System.out.println("index of COL_NAME is " + ArrayIndex.COL_NAME.getIndex());
}
0 голосов
/ 07 марта 2012

Я также столкнулся с этой проблемой, и, придя из мира C ++, я не ожидал столкнуться с этим.

Прелесть enum в том, что она может создавать серию связанных констант с уникальными значениямигде фактическое значение не имеет значения.Их использование делает код более легким для чтения и защищает от установки переменных на недопустимые значения перечисления (особенно в Java), но в такой ситуации это большая проблема.Хотя у вас может быть «enum Pets {CAT, BIRD, DOG}» и вам все равно, какие значения на самом деле представляют CAT, BIRD и DOG, написать так приятно и чисто: myPets [CAT] = "Dexter";myPets [BIRD] = "Полли";MyPets [DOG] = "Бу-Рух";

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

private int getPetsValue(Pets inPet) {

    int value = 0; 
    switch (inPet) {

        case CAT:    value = 0;    break;
        case BIRD:   value = 1;    break;
        case DOG:    value = 2;    break;
        default:
            assert(false);
            value = 0;
            break;
    }
    return value;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...