Как реализованы перечисления в Java?

В последнее время задаюсь вопросами относительно реализации некоторых типов данных, которые фактически являются частью самого языка. Не так давно разбирался со стандартными массивами, теперь пришло время разобраться и с перечислениями.

Если я правильно понял, то любой еnum является классом, пускай и не совсем обычным. Мы не можем явно наследоваться от какого бы то ни было класса, если мы объявили наш класс, как enum, но мы должны понимать, что неявным образом мы всё равно наследуемся от абстрактного класса java.lang.Enum, что подтверждает вызов цепочки методов getClass().getSuperclass() на объекте, который был объявлен элементом перечесления. Опытным путём было выявлено, что все перечисления представляют собой общедоступные статические final-переменные, имеющие тип описываемого класса и тут же проинициалированные объектами соответствующего типа. Небольшой пример:

public enum Seasons {
    WINTER, SPRING, SUMMER, FALL
}

Можно сказать, что грубо это будет равносильно следующему:

public class Seasons extends Enum {
    public static final Seasons WINTER = new Seasons();
    public static final Seasons SPRING = new Seasons();
    public static final Seasons SUMMER = new Seasons();
    public static final Seasons FALL = new Seasons();
}

Если мы хотим определить какие-то свои методы для конкретного элемента перечисления, то это будет реализовано при помощи анонимного класса. Соответственно, если метод не был определён в теле самого класса, но был определён для конкретного элемента, то мы всё равно не сможем его вызвать из-за особенностей полиморфизма, так как наш объект будет восприниматься уже не как экземляр некоторого анонимного класса, а как некоторое обобщение для всех элементов перечисления.

Собственно, теперь хотелось бы подойти к самой сути вопроса. Вроде бы во всём разобрался, только вот вопрос с переменными String name и int ordinal немного поставил меня в тупик. Что я имею в виду? В классе java.lang.Enum объявлены приватные переменные с идентификаторами name и ordinal. Их значения могут быть возвращены при вызове одноимённых методов: name() и ordinal(). Также эти переменные фигурируют в единственном конкструкторе, который был определён в данном абстрактном классе. И вот здесь у меня возникает вопрос. Очевидно, что для наших классов-перечислений обязательно должен генерироваться конструктор, который будет вызывать конструтор суперкласса и передавать ему параметры для инициализации этих полей. Я задумался над тем, как это молго бы быть реализовано в коде, но не нашёл ответа. Каким образом считывается имя нашей переменной и потом преобразовывается в строку? И каким образом мы получаем данные для переменной ordinal, если в наших классах-перечислениях отсутствуют какие-то дополнительные переменные, которые помогли бы в этих вычислениях? Просто хотелось бы понять, как это реализовано на программном уровне и как примерно можем выглядеть стандартный конструктор (пускай мы и не можем его вызвать) для класса-перечисления. Хотелось бы узнать это для саморазвития, так как очень люблю понимать, что у нас находится "под двигателем". Всем огромное спасибо за помощь! :)


Ответы (1 шт):

Автор решения: Barmaley Red Star

Под капотом Enum действительно реализуется как класс, который наследуется от класса public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable, правда с одной оговоркой, класс недоступен простым смертным и используется компилятором для синтеза класса, который будет из себя далее изображать Enum

Исходники здесь

Как нетрудно заметить, у него есть конструктор:

 /**
 * Sole constructor.  Programmers cannot invoke this constructor.
 * It is for use by code emitted by the compiler in response to
 * enum type declarations.
 *
 * @param name - The name of this enum constant, which is the identifier
 *               used to declare it.
 * @param ordinal - The ordinal of this enumeration constant (its position
 *         in the enum declaration, where the initial constant is assigned
 *         an ordinal of zero).
 */
protected Enum(String name, int ordinal) {
    this.name = name;
    this.ordinal = ordinal;
}

который принимает name и ordinal, которые опять же (если не заданы программистом) то синтезируются компилятором: name - совпадает с идентификатором константы Enum, ordinal - порядковый номер константы в списке деклараций

→ Ссылка