Что делает enum в java несовместимым?

Я знаю, что перечисление

enum Year
{
   First, Second, Third, Fourth;
}

преобразуется в

final class Year extends Enum<Year>
{
        public static final Year First = new Year();
        public static final Year Second = new Year();
        public static final Year Third = new Year();
        public static final Year Fourth = new Year();
}

Когда я пытался создать экземпляр enum (не класса), я получил ошибку времени компиляции как:

error: enum types may not be instantiated
        Year y = new Year();

Как известно, частный конструктор делает класс несовместимым. И я думал, что компилятор предоставляет частный конструктор. Но снова я смутился, увидев, что мы можем определить конструктор для перечисления с модификатором по умолчанию и до сих пор не может создать объект типа enum.

enum Year
{
        First, Second, Third, Fourth;
        Year()
        {
        }
}

class Example
{
        public static void main(String[] args)
        {
                Year y = new Year();
        }
}

Мое сомнение в том, что, если речь идет не о конструкторах, то что делает enum в Java неинтересным?

Ответ 1

Он указан в Спецификации языка Java:

8.9. Типы перечислений

...

Тип перечисления не имеет экземпляров, отличных от тех, которые определены его константами перечисления. Это ошибка времени компиляции, чтобы попытаться явно создать экземпляр типа перечисления (§15.9.1).

Следовательно, компилятор гарантирует выполнение этого требования. Поскольку компилятор "знает", что тип является enum, он может различать enum Year и final class Year.

Кроме того, для конструктора перечисления не допускается модификатор доступа:

8.9.2. Объявления об объявлении чата

...

Это ошибка времени компиляции, если объявление конструктора в объявлении перечисления является общедоступным или защищенным.

...

В объявлении перечисления объявление конструктора без модификаторов доступа является закрытым.

Итак, на практике конструктор enum выглядит как пакетный (без модификатора доступа), но он действительно является частным.

Наконец, в том же разделе также указано

В объявлении перечисления без объявлений конструктора неявно объявляется конструктор по умолчанию. Конструктор по умолчанию является закрытым, не имеет формальных параметров и не имеет предложения throw.

Это делает недействительным enum, даже если конструктор явно не объявлен.

Ответ 2

An enum в java имеет конструктор по умолчанию, когда он не определен и закрыт.

Модификатор по умолчанию имеет разные значения в разных областях. Например, внутри модификатора доступа по умолчанию для методов и полей есть package private.

Где, как в модификаторе доступа по умолчанию, используется public. На самом деле в поле интерфейса не может быть никакого другого модификатора, поэтому он неявно public.

В классе верхнего уровня он закрыт пакетом private (где разрешено только 2 модификатора доступа public и пакет по умолчанию)

Итак, ответ на ваш вопрос заключается в том, что компилятор так решает. Писатель-компилятор должен был поддерживать контракт на разработку языка.

Вы правы, думая, что это обычный класс после всего. Каждый тип чертежа объекта в java - это класс, который может быть представлен java.lang.Class. Эти ограничения для интерфейсов, перечислений, абстрактных классов, анонимных классов, локальных классов метода проверяются только компилятором.

Если вы можете каким-то образом избежать компилятора и сгенерировать свой собственный байтовый код для перечислений или другим способом, если вы можете изменить байтовый код сгенерированного класса enum, чтобы он стал публичным конструктором, вы могли бы позвонить это конструктор вне области enum private scope. Вы также можете попробовать экспериментировать с отражением, чтобы сделать то же самое. Фактически, генерируя байт-код вручную, языки JVM, такие как Groovy, Jython, JRuby, Clojure, могут предоставлять функциональные возможности, которые не находятся в самой Java. Они обходят java-компилятор.

Назначение для конструктора в enum состоит в том, чтобы иметь возможность устанавливать поля констант в один вызов. Все константы внутри enum являются экземплярами класса enum, поэтому они также содержат объявленные в нем поля.

enum Test
{
    T1(1), // equivalent to public static final Test T1 = new Test(1);
    T2(2); // equivalent to public static final Test T2 = new Test(2);

    int id;
    Test(int id)
    {
        this.id = id;
    }
}

И, наконец, вывести декомпилированный код выше enum с помощью java -p Test.class

final class Test extends java.lang.Enum<Test>
{
    public static final Test T1;
    public static final Test T2;
    int id;
    private static final Test[] $VALUES;
    public static Test[] values();
    public static Test valueOf(java.lang.String);
    private Test(int);
    static {};
}

Он должен лучше понять, что происходит, когда компилируется класс.