Всем привет!
Хочу рассказать про ряд неочевидных особенностей 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 {
void show() {}
},
RIGTH;
void show() {}
}
Если посмотреть его байткод, упросить его и мысленно декомпилировать обратно в Java, то получится:
public class Direction extends Enum {
public static final Direction LEFT = new Direction() {
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