Перечисления

  1. Что такое перечисления?
  2. Использование в операторе switch
  3. Класс java.lang.Enum
  4. Возможности перечисления
  5. Объявление перечислений
  6. Переопределение методов
  7. Перечисления и интерфейсы

1. Что такое перечисления?

В Java 5 были введены перечисления, которые создаются с использованием ключевого слова enum. Перечисления указывают возможные значения для какого-то явления. Например, вы открыли кофейню, в которой продаются три возможные варианты кофе - BIG, HUGE и OVERWHELMING. Других вариантов быть не может. Если задавать значения с помощью String, можно выбрать любое другое значение, например - MIDDLE, SMALL. Задавая перечисления, вы ограничиваете возможные варианты:

public enum CoffeeSize {
    BIG, HUGE, OVERWHELMING
}

В простейшей форме перечисления - это список именованных констант. Каждая константа перечисления (BIGHUGE и OVERWHELMING) является объектом класса, в котором она определена. Константы перечисления являются static final и не могут быть изменены после создания.

Перечисления можно представить в виде класса, содержащего константы, например:

public class CoffeeSize5 {
    public static final String BIG = "BIG";
    public static final String HUGE = "HUGE";
    public static final String OVERWHELMING = "OVERWHELMING";
}

Но у перечислений гораздо больше преимуществ по сравнению с таким классом. Какие - рассмотрим чуть позже.  

Можно создавать переменные типа перечисления. При этом не используется оператор new. Переменная перечисления объявляется и применяется практически так же, как и переменные примитивных типов:
public class CoffeeSizeDemo1 {
    public static void main(String[] args) {
        CoffeeSize coffeeSize = CoffeeSize.BIG;
        if (coffeeSize == CoffeeSize.BIG) {
            System.out.println(coffeeSize);
        }
    }
}

2. Использование в операторе switch

Значения перечислимого типа можно также использовать в управляющем операторе switch. В выражениях ветвей case должны использоваться константы из того же самого перечисления, что и в самом операторе switch. В выражениях ветвей case имена констант указываются без уточнения имени их перечислимого типа. Тип перечисления в операторе switch уже неявно задает тип enum для операторов case.

public class CoffeeSizeDemo2 {
    public static void main(String[] args) {
        CoffeeSize coffeeSize = CoffeeSize.BIG;
        switch (coffeeSize) {
            case BIG:
                System.out.println("Дайте мне большую чашку кофе!");
                break;
            case HUGE:
                System.out.println("Дайте мне огромную чашку кофе!");
                break;
            case OVERWHELMING:
                System.out.println("Дайте мне громадную чашку кофе!");
                break;
            default:
                System.out.println("Чашка не выбрана.");
        }
    }
}

3. Класс java.lang.Enum

Перечисление в Java относится к типу класса, но перечисление НЕ может наследоваться от другого класса и НЕ может быть суперклассом.

Все перечисления автоматически наследуют от класса java.lang.Enum. В этом классе определяется ряд методов, доступных для использования во всех перечислениях: ordinal(), compareTo(), equals(), values() и valueOf().

Перечисления также неявно наследуют интерфейсы Serializable и Comparable.

Рассмотрим методы класса java.lang.Enum.

3.1. Метод values()

Метод values() возвращает массив, содержащий список констант перечислимого типа:

public class CoffeeSizeValuesDemo {
    public static void main(String[] args) {
        System.out.println("Константы перечислимого типа CoffeeSize:");
        CoffeeSize[] coffeeSizes = CoffeeSize.values();
        for (CoffeeSize c : coffeeSizes) {
            System.out.println(c);
        }
    }
}

Результат выполнения:

Константы перечислимого типа CoffeeSize:
BIG
HUGE
OVERWHELMING

3.2. Метод valueOf()

Статический метод valueOf() возвращает константу перечислимого типа, значение которой соответствует символьной строке, переданной в качестве аргумента. Можно сказать, что этот метод преобразует значение String в перечисление:

public class CoffeeSizeDemo3 {
    public static void main(String[] args) {
        CoffeeSize coffeeSize = CoffeeSize.valueOf("BIG");
        System.out.println("Переменная coffeeSize содержит " + coffeeSize);
    }
}

3.3. Метод ordinal()

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

public class CoffeeSizeDemo4 {
    public static void main(String[] args) {
        for (CoffeeSize c : CoffeeSize.values()) {
            System.out.println(c + " " + c.ordinal());
        }
    }
}

3.4. Метод compareTo()

С помощью метода int compareTo(типПеречисления e) можно сравнить порядковые значения двух констант одного и того же перечислимого типа. Метод возвращает значение типа int.

Если порядковое значение вызывающей константы меньше, чем у константы е (this < e), то метод compareTo() возвращает отрицательное значение.

Если порядковые значения обеих констант одинаковы (this == e), возвращается нуль.

Если порядковое значение вызывающей константы больше, чем у константы е (this > e), то возвращается положительное значение.

public class CoffeeSizeCompareTo {
    public static void main(String[] args) {
        CoffeeSize bigCup = CoffeeSize.BIG;
        CoffeeSize hugeCup = CoffeeSize.HUGE;
        CoffeeSize anotherBigCup = CoffeeSize.BIG;
        CoffeeSize overwhelmingCup = CoffeeSize.OVERWHELMING;

        System.out.println("bigCup.compareTo(hugeCup): " + bigCup.compareTo(hugeCup));
        System.out.println("hugeCup.compareTo(bigCup): " + hugeCup.compareTo(bigCup));
        System.out.println("bigCup.compareTo(anotherBigCup): " + bigCup.compareTo(anotherBigCup));
        System.out.println("bigCup.compareTo(overwhelmingCup): " + bigCup.compareTo(overwhelmingCup));
    }
}

Результат выполнения кода:

bigCup.compareTo(hugeCup): -1
hugeCup.compareTo(bigCup): 1
bigCup.compareTo(anotherBigCup): 0
bigCup.compareTo(overwhelmingCup): -2

3.5. Метод equals()

Вызвав метод equals(), переопределяющий аналогичный метод из класса Object, можно сравнить на равенство константу перечисления с любым другим объектом. Но оба эти объекта будут равны только в том случае, если они ссылаются на одну и ту же константу из одного и того же перечисления. Простое совпадение порядковых значений не вынудит метод equals() возвратить логическое значение true, если две константы относятся к разным перечислениям. 

При сравнении констант перечислений, можно использовать оператор "==" - он будет работать также, как и метод equals().

public class CoffeeSizeEquals {
    public static void main(String[] args) {
        CoffeeSize bigCup = CoffeeSize.BIG;
        CoffeeSize hugeCup = CoffeeSize.HUGE;
        CoffeeSize anotherBigCup = CoffeeSize.BIG;

        System.out.println("bigCup.equals(hugeCup): " + bigCup.equals(hugeCup));
        System.out.println("bigCup.equals(anotherBigCup): " + bigCup.equals(anotherBigCup));
        System.out.println("bigCup == anotherBigCup: " + (bigCup == anotherBigCup));
    }
}

4. Возможности перечисления

Создать экземпляр перечисления с помощью оператора new нельзя, но в остальном перечисление обладает всеми возможностями, которые имеются у других классов. А именно - в перечисления можно добавлять конструкторы, переменные и методы. Конструкторы перечисления являются private по умолчанию.

Допустим мы хотим задать размер нашей чашки кофе в миллилитрах. Для этого введем переменную ml в перечисление и геттер метод getMl(). Добавим конструктор, на вход которого будем задавать значение для миллилитров.

Обратите внимание, что при объявлении конструктора не указан модификатор доступа - он private по умолчанию. Уже говорилось, что нельзя создавать объекты перечисления используя оператор new. Как же тогда вызвать наш конструктор? Для вызова конструктора перечисления после указания константы ставятся круглые скобки, в которых передается нужное значение.

public enum CoffeeSize2 {
    // 100, 150 и 200 передаются в конструктор
    BIG(100), HUGE(150), OVERWHELMING(200);

    private int ml;

    CoffeeSize2(int ml) {
        this.ml = ml;
    }

    public int getMl() {
        return ml;
    }
}

Методы для перечисления вызываются так же, как и для обычного объекта. В следующем классе мы перебираем все константы нашего перечисления и для каждого вызываем метод getMl():

public class CoffeeSizeMlDemo {
    public static void main(String[] args) {
        for (CoffeeSize2 coffeeSize : CoffeeSize2.values()) {
            System.out.println(coffeeSize + " " + coffeeSize.getMl());
        }
    }
}

Конструкторы в перечислении могут быть перегружены, как показано в следующем примере. Для вызова конструктора без параметров просто не пишите скобки после константы:

public enum CoffeeSize3 {
    BIG(100), HUGE, OVERWHELMING(200);

    private int ml;

    CoffeeSize3(int ml) {
        this.ml = ml;
    }

    CoffeeSize3() {
        this.ml = -1;
    }

    public int getMl() {
        return ml;
    }
}

5. Объявление перечислений

Перечисления могут быть объявлены: отдельным классом или как член класса. Но НЕ могут быть объявлены внутри метода.

В этом пример перечисление CoffeeSize объявлено внутри класса Coffee3:

public class Coffee3 {
    enum CoffeeSize { BIG, HUGE, OVERWHELMING }

    CoffeeSize size;
}

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

public class CoffeeTest2 {
    public static void main(String[] args) {
        Coffee3 drink = new Coffee3();
        drink.size = Coffee3.CoffeeSize.BIG; // имя внешнего класса необходимо
    }
}
public class CoffeeTest3 {
    public static void main(String[] args) {
        // Неправильно! Нельзя объявлять перечисления внутри метода!
        /*enum CoffeeSize {BIG, HUGE, OVERWHELMING}
        Coffee drink = new Coffee();
        drink.size = CoffeeSize.BIG;*/
    }
}

6. Переопределение методов

Для перечислений можно переопределять методы, но это не совсем обычное переопределение.

Добавим в наше перечисление метод getLid(), который возвращает код крышки для чашки кофе. Для всех констант подходит код B, который возвращает этот метод, кроме константы OVERWHELMING. Для OVERWHELMING чашки нужен код A. Переопределим метод getLid() для этой константы. Как это делается? После объявления константы открываем фигурные скобки, в которых и переопределяем этот метод. Если необходимо переопределить несколько методов, это делается в этих же фигурных скобках.

public enum CoffeeSize4 {
    BIG(100),
    HUGE(150),
    OVERWHELMING(200) {
        @Override
        public String getLidCode() {
            return "A";
        }
    };

    private int ml;

    CoffeeSize4(int ml) {
        this.ml = ml;
    }

    public int getMl() {
        return ml;
    }

    public String getLidCode() {
        return "B";
    }
}
public class CoffeeSizeDemo7 {
    public static void main(String[] args) {
        for (CoffeeSize4 coffeeSize : CoffeeSize4.values()) {
            System.out.println(coffeeSize + " " + coffeeSize.getLidCode());
        }
    }
}

Результат выполнения кода:

BIG B
HUGE B
OVERWHELMING A

7. Перечисления и интерфейсы

Перечисления не могут наследовать другие классы, но могут реализовывать интерфейсы. Например, следующее перечисление реализует интерфейс Runnable

public enum Currency implements Runnable {
    PENNY(1), NICKLE(5), DIME(10), QUARTER(25);
    private int value;

    Currency(int value) {
        this.value = value;
    }

    @Override
    public void run() {
        System.out.println("Перечисления в Java могут реализовывать интерфейсы");
    }
}

 

Read also:
Comments