Назначение метода public public enum Java

Итак, у меня есть enum

public enum Sample {
   ValueA{
      @Override
   public String getValue(){ return "A"; }   
 },
 ValueB{
    @Override
   public String getValue(){ return "B"; }

   public void doSomething(){ }
 };
abstract public String getValue();
};

и у меня есть другой код, пытающийся использовать перечисление.

Sample.ValueB.doSomething();

Кажется, что он должен быть действительным, но вызывает ошибку "Метод doSomething() - undefined для типа Sample". В отличие от

Sample value = Sample.ValueB;
value.doSomething();

который производит ту же ошибку и представляется разумным.

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

Ответ 1

Тип "поля" ValueA равен Sample. Это означает, что вы можете вызывать только методы на ValueA, которые предоставляет Sample. Из JLS §8.9.1. Константы контировки:

Методы экземпляров, объявленные в этих телах классов, могут вызываться вне охватывающего типа перечисления только в том случае, если они переопределяют доступные методы в охватывающем типе перечисления.

Что еще более важно: с точки зрения дизайна enum значения должны быть одинаковыми: если какая-либо операция возможна с одним конкретным значением, тогда это должно быть возможно со всеми значениями (хотя это может привести к выполнению разного кода).

Ответ 2

В основном тип времени компиляции Sample.ValueB по-прежнему является образцом, хотя тип времени выполнения для этого значения будет ValueB. Таким образом, ваши два фрагмента кода эквивалентны - клиенты не видят "дополнительные" методы, которые присутствуют только в некоторых ваших значениях перечисления.

Вы можете эффективно подумать об enum как объявлении такого поля:

public static final Sample ValueB = new Sample() {
    @Override
    public String getValue(){ return "B"; }

    public void doSomething(){ }
};

Ответ 3

Когда вы пишете

enum Sample
{
     Value{
        public void doSomething()
        {
              //do something
        }
    };
}

вы не создаете экземпляр Sample enum, а скорее экземпляр анонимного подкласса enum Sample. То, почему метод doSomething() равен undefined.

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

Попробуйте это

System.out.println(Sample.ValueB.getClass().getName());

печатает Sample$2

Это означает, что даже если у вас есть метод doSomething() в экземпляре Sample$2, который вы назвали ValueB, вы ссылаетесь на него через ссылку суперкласса типа Sample и, следовательно, только те методы определенные в классе Sample, будут видны во время компиляции.

Вы можете называть это doSomething() во время выполнения посредством отражения.

Ответ 4

public void doSomething(){ } будет иметь тот же эффект, что и private void doSomething(){ }.

doSomething() не отображается вне ValueB, если вы не добавите doSomething() в Sample.

Каждое значение Sample имеет тип Sample, поэтому он не может иметь различный набор методов для разных значений (с внешней точки зрения).