Найти в Дзене
(java || kotlin) && devOps

enum в Java: очевидное - невероятное

Всем привет!

Хочу рассказать про ряд неочевидных особенностей enum в Java.

Поехали!

1) enum - это полноценный класс, у него могут быть поля, методы, обычные и статические

2) любой enum неявно (!) расширяет абстрактный класс Enum, поэтому наследовать enum от кого-то другого нельзя. Например, один enum от другого. При этом добавить final для enum нельзя, т.к. он уже фактически final)

3) зато enum может реализовывать интерфейсы, как и любой другой класс в Java

4) самое интересное - каждое значение enum является наследником самого enum. Т.е. можно объявить метод show в enum Direction и переопределить его Direction.LEFT. На первый взгляд противоречит пункту 2, но см. дальше

5) Как устроен enum внутри?

Возьмем такой enum:

enum Direction {

LEFT {

@Override

void show() {}

},

RIGTH;

void show() {}

}

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

public class Direction extends Enum {

public static final Direction LEFT = new Direction() {

@Override

void show() {

}

};

public static final Direction RIGTH = new Direction();

private static final Direction[] VALUES = values();

private Direction(String name, int ordinal) {

super(name, ordinal);

}

public static Direction[] values() {

return VALUES.clone();

}

public static Direction valueOf(String name) {

return Enum.valueOf(Direction.class, name);

}

void show() {

}

}

Т.е. enum превращается в класс со статическими public полями. И у класса создаются анонимные классы-наследники.

Собственно, если бы в Java не было enum - я бы реализовал перечисление примерно также

6) enum не может иметь public конструктор, и метод clone для него возвращает CloneNotSupportedException. И как я говорил выше он фактически финальный. Следовательно, во время работы программы число объектов enum равно число значений enum

7) из-за этого с помощью enum легко можно реализовать singleton. Можно, но не нужно, если вы используете Spring или другой IoC контейнер) Но об этом как-нибудь позже

8) исходя из сказанного выше enum-ы можно и нужно сравнивать по ==. Во-первых это нагляднее, а во-вторых меньше шансов получить NPE неправильно написав код сравнения.

9) хочу обратить внимание на классы EnumSet и EnumMap, которые эффективно с точки зрения расхода памяти позволяют использовать значения Enum в качестве ключей. Enum реализует Comparable, сравнение идет по полю ordinal - порядковому номеру значения.

10) Enum реализует Serializible и успешно сериализуется. Но есть одна важная особенность. При сериализации конкретного объекта сохраняется лишь его имя, и объект восстанавливается потом вызовом valueOf. Значения других полей теряются. Следовательно, enum должен быть реализован иммутабельным, значения всех полей должны задаваться в конструкторе.

#java