Javenue logo

Javenue

Программирование на Java

Информационные технологии

Перечисляемые типы в Java - enum

[О перечислениях в C++ можно прочитать здесь - Перечисления в C++.]

По просьбам читателей начну цикл статей, посвященный нововведениям в Java начиная с версии 1.5. Думаю, статьи этой тематики будут актуальны для многих, так как сам только недавно начал переходить на 1.6 (на работе в проекте до сих пор используется версия jdk 1.4).

В этой статье я хочу рассказать о перечислениях (enumeration, enum). Сначала я покажу, как реализовывались перечисления в ранних версиях java.

Итак, перечисление (enum) - это тип, значения которого ограничены конечным набором констант. Самым простым способом реализовать некоторый набор значений есть следующий класс:

public final class Modifiers {
    public static final int PUBLIC = 1;
    public static final int STATIC = 2;
    public static final int FINAL = 3;
}

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

  • небезопасность (type unsafe): в методе, где требуется передать значение перечисления, можно передать любое число, а не только 1, 2 или 3;
  • неинформативность: например, при отладке вывод элемента перечисления не скажет нам ни о чем;
  • подверженость ошибкам: например, при добавлении нового элемента или при изменении последовательности существующих.

Хуже этого подхода может быть только использование строк вместо integer. Тогда к вышеперечисленным проблемам добавляется еще и возможная потеря производительности при сравнении двух элементов.

Реализация type safe enum может быть, например, такой (желательно, чтобы Enum еще был Serializable и Comparable):

public abstract class Enum {
    protected int id;
    protected String name;

    protected Enum(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return this.id;
    }

    public String getName() {
        return name;
    }

    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || !(o instanceof Enum)) return false;
        final Enum enumer = (Enum) o;
        return (id == enumer.id);
    }

    public int hashCode() {
        return id;
    }

    public String toString() {
        return getClass().getName() + "[id=" + id 
                + ", name=" + name + "]";
    }

    protected static Enum getByName(Enum[] enums, String name) {
        if (name != null) {
            for (int i = 0; i < enums.length; i++) {
                if (enums[i].name.equalsIgnoreCase(name)) {
                    return enums[i];
                }
            }
        }
        return null;
    }

    protected static Enum getById(Enum[] enums, int id) {
        for (int i = 0; i < enums.length; i++) {
            if (enums[i].id == id) {
                return enums[i];
            }
        }
        return null;
    }
}

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

Вот правильный пример перечилсения, приведенного в начале статьи:

public class Modifiers extends Enum {
    public static final int PUBLIC_ID = 1;
    public static final String PUBLIC_VIEW = "public";
    public static final Modifiers PUBLIC = 
        new Modifiers(PUBLIC_ID, PUBLIC_VIEW);

    public static final int STATIC_ID = 2;
    public static final String STATIC_VIEW = "static";
    public static final Modifiers STATIC = 
        new Modifiers(STATIC_ID, STATIC_VIEW);

    public static final int FINAL_ID = 3;
    public static final String FINAL_VIEW = "final";
    public static final Modifiers FINAL = 
        new Modifiers(FINAL_ID, FINAL_VIEW);

    public static final Modifiers[] modifiers = new Modifiers[]{
        PUBLIC, STATIC, FINAL
    };

    private Modifiers(int id, String name) {
        super(id, name);
    }

    public static Modifiers getByView(String code) {
        return (Modifiers) getByName(modifiers, code);
    }

    public static Modifiers getById(int id) {
        return (Modifiers) getById(modifiers, id);
    }
}

Для хранения в базе данных и сравнения можно использовать ID, а для отладочной информации, логирования или user-friendly отображения используется VIEW.

С выходом jdk версии 1.5 жить стало намного проще. Кроме того, что была предоставлена реализация абстрактного класса Enum (java.lang.Enum), так же для простоты ввели лингвистическую конструкцию enum.

Вернемся к нашему примеру:

public enum Modifiers {
    PUBLIC, STATIC, FINAL
}

Проще некуда, правда?

Запись "public enum Modifiers" равноценна "abstract class Modifiers extends java.lang.Enum". Хотя класс вроде как объявлен без модификатора final, дальше расширять наш класс мы уже не можем (этот запрет реализован на уровне компилятора), но можем добавить в него необходимую нам функциональность. А именно: добавить поля, конструкторы, методы, статические методы. Пример (не пытайтесь найти в нем смысл):

public enum Modifiers {
    PUBLIC(1), STATIC(2), FINAL(3);

    Modifier(int id) {
        this.id = id;
    }

    private int id;

    public int getId() {
        return id;
    }

    public static String getClassName() {
        return Modifiers.class.getName();
    }
}

Заметьте, что конструктор может быть либо приватным, либо package-private. Для получения дополнительной информации по данной теме обратитесь к реализации абстрактного класса java.lang.Enum. Стоит обратить внимание, что многие методы, переопределяющие методы класса Object, объявлены как final. Так же, для enum не разрешено клонирование.

Если у вас есть какие-либо вопросы, касающиеся использования перечислений, буду рад вам помочь.



Комментариев: 0

  Выйти

  * для публикации комментариев нужно