2014-11-05 2 views
3

У меня есть перечисление, которое может выглядеть так, как показано ниже. Моя цель состоит в том, чтобы иметь enum с некоторыми распространенными методами (я применял это, добавляя абстрактный метод) и некоторые методы «enum value individual».Как вызвать Enum индивидуальные методы?

Следующий код компилирует:

public enum MyEnum { 

    VALUE { 
     @Override 
     public void syso() { 
      System.out.println("VALUE syso"); 
     } 
    }, 

    SPECIAL_VALUE { 
     @Override 
     public void syso() { 
      System.out.println("SPECIAL_VALUE syso"); 
     } 

     public void sayHello() { 
      System.out.println("Hello"); 
     } 
    }; 

    public abstract void syso(); 

    public static void main(String... args) { 
     MyEnum.VALUE.syso(); 
     MyEnum.SPECIAL_VALUE.syso(); 
    } 

} 

Запуск это приводит к следующему печатаемого:

VALUE syso 
SPECIAL_VALUE syso 

Однако пытается вызвать sayHello(), который я определил в SPECIAL_VALUE, не работает.

Добавление следующего к основной методе, не составляет больше:

public static void main(String... args) { 
    MyEnum.SPECIAL_VALUE.sayHello(); // does not work 
} 

Почему? Мне кажется, это прекрасно, но метод не найден. Есть ли способ вызвать этот метод? Может быть, через отражение?

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

Можно ли это запустить? Если нет, какова будет моя лучшая альтернатива?

+0

вы обязательно сможете назвать это через отражение –

+1

Не объявлять метод абстрактно, просто сделайте его пустым. И в 'SPECIAL_VALUE' переопределить его. Я думаю, что можно использовать рефлексию, но почему? –

+0

@AndrewLogvinov Реализация этого пустым способом также приведет к тому, что я могу назвать 'MyEnum.VALUE.sayHello()', что ничего не сделало бы. Это не похоже на хорошую идею. – noone

ответ

6

Почему?

Причина дается в JLS:

8.9.1. Enum Константа метода

...

Instance, объявленная в классе перечисления органов может быть вызвана вне вмещающих перечислений типа только если они переопределяют доступные методы типа вмещающих перечислений (§8.4.8).

Есть ли способ, чтобы вызвать этот метод? Может быть, через отражение?

Учитывая указанное ограничение, отражение является единственной альтернативой, если вы не хотите раскрывать метод в охватывающем классе enum. Каждая константа перечисления создается как внутренний класс, например MyEnum$1 и MyEnum$2 в вашем примере.Таким образом, вы можете получить Class через getClass() метода константы, а затем вызов методы посредством отражения (обработка исключений опущена):

... 
Class c = MyEnum.SPECIAL_VALUE.getClass(); 
Method m = c.getMethod("sayHello"); 
m.invoke(MyEnum.SPECIAL_VALUE); 
... 

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

... 
public void sayHello() { 
    throw new UnsupportedOperationException(); 
} 
... 

Это по крайней мере, уловы непреднамеренных вызовов во время выполнения - это еще не позволяет компилятору поймать их во время компиляции, но и не делает подход отражения.

+0

Спасибо, я думаю, это объясняет это достаточно хорошо. Я думаю, что я пойду с вашим решением, реализующим его, бросив «UnsupportedOperationException» и переопределив это в «SPECIAL_VALUE». Я все еще не понимаю причины этого утверждения в JLS. – noone

+0

@noone Что говорит JLS, имеет смысл, если вы думаете о том, что единственные общедоступные интерфейсы имеют тот, который объявлен самим классом enum, а это значит, что компилятор не знает о «специальных» методах, которые вы заявляете в только один/некоторые из ваших констант в клиентском коде. В других ситуациях, когда вы работаете с «нормальными» классами, вы обходите это с помощью downcasting, но поскольку перечисления, реализованные в этом конкретном стиле, являются анонимными классами, вы не можете выполнить такой подбор (без отражения (и I) м даже не уверен, что это может сработать)). – awksp

1

Поскольку SPECIAL_VALUE является экземпляром enum и enum имеет только метод syso() .Вы звоните

Вот то же самое с классами:

interface Foo { 

     Foo inst1 = new Foo() { 
      @Override 
      public void doFoo() { 
      } 

      public void doAnonymous() { 

      } 
     }; 
     void doFoo(); 
    } 

Вы не можете вызвать метод doAnonymous() как Foo. inst1.doAnonymous(), и вы можете получить доступ к doAnonymous только с помощью отражения

+0

Ну, я не говорю 'MyEnum.sayHello()', я явно говорю 'MyEnum.SPECIAL_VALUE.sayHello()'. Поэтому, в отличие от вашего кода, я все же даю компилятору хотя бы намек на «подтип». MyEnum.SPECIAL_VALUE не похож на «анонимный» подтип, так как я явно даю ему имя. – noone

+0

Ну, да, но компилятор рассматривает его как экземпляр MyEnum. И MyEnum не имеет метода sayHello(). – Heisenberg

+0

Да, это определенно кажется таким. Разработчики компилятора Java, вероятно, слишком ленивы, чтобы сделать эту работу. :) – noone

Смежные вопросы