Урок 10

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

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

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

Пример 1. Перечисление

public enum CoffeeSize {
    BIG, HUGE, OVERWHELMING
}

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

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

Пример 2. Использование констант вместо перечисления

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

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

Можно создавать переменные типа перечисления. При этом не используется оператор new. Переменная перечисления объявляется и применяется практически так же, как и переменные примитивных типов:

Пример 3. Создание переменных перечисления

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.

Пример 4. Использование перечисления в операторе switch

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() возвращает массив, содержащий список констант перечислимого типа:

Пример 5. Использование метода 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 в перечисление.

Пример 6. Использование метода valueOf()

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

3.3. Метод ordinal()

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

Пример 7. Использование метода 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), то возвращается положительное значение.

Пример 8. Использование метода compareTo()

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(). 

Пример 9. Использование метода 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 по умолчанию.

Пример 10. Использование конструктора, переменной и метода в перечислении

Допустим мы хотим задать размер нашей чашки кофе в миллилитрах. Для этого введем переменную 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());
        }
    }
}

Пример 11. Использование перегружаемых форм конструкторов

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

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. Объявление перечислений

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

Пример 12. Объявление перечисления в качестве члена класса

В этом пример перечисление 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; // имя внешнего класса необходимо
    }
}

Пример 13. Объявление перечисления в методе

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() для этой константы. Как это делается? После объявления константы открываем фигурные скобки, в которых и переопределяем этот метод. Если необходимо переопределить несколько методов, это делается в этих же фигурных скобках.

Пример 14. Переопределение метода перечисления

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: 

Пример 15. Перечисление реализующее интерфейс

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 могут реализовывать интерфейсы");
    }
}

 

Источники: Java Enum Tutorial: 10 Examples of Enum in Java Read more




0 Comments
Leave your comment: