Подтвердить что ты не робот

Как расширить класс enum из абстрактного класса?

Что-то вроде этого:

public enum Token
{
     FOO("foo", "f"),
     QUIT("quit", "q"),
     UNKNOWN("", "");
     ...

     public parse(String s) {
         for (Token token : values()) {
              ...
              return token;
         }
         return UNKNOWN;
     }
}

Абстрактный класс:

abstract class Base 
{
    private boolean run;

    Base() {
        run = true;

        while (run) {
             inp = getInput();
             act(inp);
        }
    }

    public boolean act(String s) {
        boolean OK = true;
        switch (Token.parse(inp)) { /* Enum */
             case FOO:
                    do_foo();
                    break;
             case QUIT:
                    run = false;
                    break;
             case UNKNOWN:
                    print "Unknown" + inp;
                    OK = false;
                    break;
             }
         }
         return OK;
    }
}

И расширитель:

class Major extends Base
{

}

Я хочу расширить act, как и в случае, если super не обрабатывает его, а затем попытайтесь обработать его в Major. Например. add PRINT_STAT("print-statistics", "ps") - но в то же время пусть класс Base обрабатывает по умолчанию как QUIT.

Это совершенно неправильный подход?

Что я сделал до сих пор, так это добавить интерфейс Обычно:

public interface BaseFace
{
      public boolean act_other(String inp);
}

И в классе Base implements BaseFace:

      case UNKNOWN:
          OK = act_other(inp);

И в классе Major:

  public boolean act_other(String inp) { 
       if (inp.equals("blah")) {
            do_blah();
            return true; 
       }
       return false;
  }

Это похоже на полезный дизайн?

И, главный вопрос:

Есть ли какой-нибудь хороший способ расширить класс Token, чтобы я мог использовать тот же подход переключения в Major, что и в Base? Я задаюсь вопросом, есть ли для этого лучший дизайн и второй, если мне нужно создать новый класс Token для Major или если я каким-то образом продлю или иным образом повторно использую существующие.


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

4b9b3361

Ответ 1

Все перечисления перечислены в Enum. В Java класс может распространяться не более чем на один класс.

Однако вы можете присвоить классу enum интерфейс.

Из этот учебник Java по типам перечисления:

Примечание. Все перечисления неявно расширяют java.lang.Enum. Поскольку класс может расширять только один родительский элемент (см. Декларирование классов), язык Java не поддерживает множественное наследование состояния (см. Множественное наследование состояния, реализации и типа), и поэтому перечисление не может распространять что-либо еще.

Изменить для Java 8:

С Java 8 интерфейс может включать методы по умолчанию. Это позволяет включать в интерфейсы методы реализации (но не состояния). Хотя основная цель этой возможности - позволить эволюцию публичных интерфейсов, вы можете использовать ее для наследования настраиваемого метода, определяющего общее поведение среди нескольких классов enum.

Однако это может быть хрупким. Если метод с той же подписью был позже добавлен в класс java.lang.Enum, он переопределит ваши методы по умолчанию. (Когда метод определен как в суперклассе класса, так и в интерфейсах, реализация класса всегда выигрывает.)

Например:

interface IFoo {
    public default String name() {
        return "foo";
    }
}

enum MyEnum implements IFoo {
    A, B, C
}

System.out.println( MyEnum.A.name() );  // Prints "A", not "foo" - superclass Enum wins

Ответ 2

Ваша проблема кажется хорошим кандидатом для Command Pattern

Хорошей практикой является использование перечисления как логической группы поддерживаемых действий. ИМО, имея единственное перечисление для группировки всех поддерживаемых действий, улучшит читаемость вашего кода. Имея это в виду, перечисление Token должно содержать все поддерживаемые типы действий

enum Token
{
   FOO("foo", "do_foo"),
   QUIT("quit", "do_quit"),
   PRINT_STATS("print", "do_print_stats"),
   UNKNOWN("unknown", "unknown")
   .....   

}

Рассмотрите возможность создания интерфейса. Актер, который определяет метод, действует как показано ниже:

public interface Actor
{
   public void act();
}

Вместо того, чтобы иметь один класс Base, который тоже может это сделать, вы можете иметь один класс для поддерживаемой команды, например,

public class FooActor implements Actor
{
    public void act()
    {
        do_foo(); //call some method like do_foo
    }
}

public class PrintActor implements Actor
{
    public void act()
    {
        print_stats(); //call some print stats
    }
}

Наконец, появится код драйвера, который будет принимать в качестве входного действия действие, которое будет выполнено, инициализировать соответствующий Актер и выполнить действие, вызвав act().

public class Driver
{
   public static void main(String[] args)
   {
      String command; // will hold the input string from the user.

      //fetch input from the user and store it in command

      Token token = Token.parse(command);

      switch(token)
      {
         case FOO:
                   new FooActor().act();
                   break;

         case PRINT_STATS:
                   new PrintActor().act();
                   break;
          .... 

      }


   }
}

Такая конструкция гарантирует, что вы можете легко добавлять новые команды, а код остается модульным.

Ответ 3

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

Map<Token, Runnable> behaviors;

Эта карта может быть легко изменена или заменена. Вы даже можете хранить некоторые наборы этих предопределенных поведений. В примере:

behaviors.get(Token.parse(inp)).run();

(здесь, конечно, нужны некоторые дополнительные проверки)

И последнее примечание: в большинстве случаев избегать наследования

Ответ 4

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