如何从抽象类扩展枚举类?

时间:2022-09-25 07:37:46

Having something like this:

有这样的事情:

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

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

An abstract class:

抽象类:

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;
    }
}

And the extender:

而扩展器:

class Major extends Base
{

}

What I want is to extend act as in if super does not handle it then try to handle it in Major. E.g. add PRINT_STAT("print-statistics", "ps") - but at the same time let the Base class handle defaults like QUIT.

我想要的是扩展act,如果super不处理它然后尝试在Major中处理它。例如。添加PRINT_STAT(“print-statistics”,“ps”) - 但同时让Base类处理默认值如QUIT。

Is this a completely wrong approach?

这是一个完全错误的方法吗?

What I have done so far is add an interface Typically:

到目前为止我所做的是添加一个接口通常:

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

And in class Base implements BaseFace:

在类Base实现BaseFace:

      case UNKNOWN:
          OK = act_other(inp);

And in class Major:

在课程专业:

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

Does this look like a usable design?

这看起来像一个可用的设计?

And, major question:

而且,主要问题:

Is there some good way to extend the Token class such that I can use the same switch approach in Major as in Base? What I wonder is if there for one is a better design and second if I have to make a new Token class for Major or if I somehow can extend or otherwise re-use the existing.

是否有一些扩展Token类的好方法,以便我可以在Major中使用与Base相同的切换方法?我想知道的是,如果有一个是更好的设计,第二,如果我必须为Major制作一个新的Token类,或者如果我以某种方式可以扩展或以其他方式重用现有的。


Edit: Point of concept is to have the Base class that I can easily re-use in different projects handling various types of input.

编辑:概念点是拥有我可以在处理各种类型输入的不同项目中轻松重用的Base类。

4 个解决方案

#1


42  

All enums implicity extend Enum. In Java, a class can extend at most one other class.

所有枚举都隐含着Enum。在Java中,一个类最多可以扩展一个其他类。

You can, however, have your enum class implement an interface.

但是,您可以让您的枚举类实现一个接口。

From this Java tutorial on Enum Types:

从这个关于枚举类型的Java教程:

Note: All enums implicitly extend java.lang.Enum. Because a class can only extend one parent (see Declaring Classes), the Java language does not support multiple inheritance of state (see Multiple Inheritance of State, Implementation, and Type), and therefore an enum cannot extend anything else.

注意:所有枚举都隐式扩展java.lang.Enum。由于类只能扩展一个父级(请参阅声明类),因此Java语言不支持多状态继承(请参阅状态,实现和类型的多重继承),因此枚举不能扩展其他任何内容。

Edit for Java 8:

编辑Java 8:

As of Java 8, an interface can include default methods. This allows you to include method implementations (but not state) in interfaces. Although the primary purpose of this capability is to allow evolution of public interfaces, you could use this to inherit a custom method defining a common behavior among multiple enum classes.

从Java 8开始,接口可以包含默认方法。这允许您在接口中包含方法实现(但不包括状态)。虽然此功能的主要目的是允许公共接口的演变,但您可以使用它来继承定义多个枚举类之间的公共行为的自定义方法。

However, this could be brittle. If a method with the same signature were later added to the java.lang.Enum class, it would override your default methods . (When a method is defined both in a class's superclass and interfaces, the class implementation always wins.)

但是,这可能很脆弱。如果稍后将具有相同签名的方法添加到java.lang.Enum类中,它将覆盖您的默认方法。 (当在类的超类和接口中定义方法时,类实现总是获胜。)

For example:

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


4  

Your problem seems a good candidate for the Command Pattern

您的问题似乎是命令模式的一个很好的候选人

It is a good practice to use an enum as a logical group of supported actions. IMO, having a single enum to group all supported actions will improve the readability of your code. With this in mind, the Token enum should contain all the supported action types

将枚举用作受支持操作的逻辑组是一种很好的做法。 IMO,只需一个enum就可以对所有支持的操作进行分组,这将提高代码的可读性。考虑到这一点,令牌枚举应包含所有支持的操作类型

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

}

Consider creating an interface Actor which defines an a method say act as shown below:

考虑创建一个接口Actor,它定义一个方法,如下所示:

public interface Actor
{
   public void act();
}

Instead of having a single Base class that does too may things, you can have one class per supported command for e.g.

您可以为每个支持的命令创建一个类,而不是单个基类也可以执行某些操作。

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
    }
}

Finally, there will be a driver code that will take in as input the action to be performed, initialize the appropriate Actor and execute the action by invoking the act() method.

最后,将有一个驱动程序代码将输入要执行的操作,初始化相应的Actor并通过调用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;
          .... 

      }


   }
}

Such a design will ensure that you can easily add new commands and the code remains modular.

这样的设计将确保您可以轻松添加新命令,并且代码保持模块化。

#3


3  

As other say here, You can't extend enum. From design perspective this solution looks like it's too tightly coupled. I would advise to use more dynamic approach for this. You can create some kind of behavior map:

正如其他人所说,你无法扩展枚举。从设计的角度来看,这个解决方案看起来太紧密了。我建议使用更动态的方法。您可以创建某种行为图:

Map<Token, Runnable> behaviors;

This map could be easily modified or replaced. You can even store some sets of those predefined behaviors. In example:

可以轻松修改或替换此地图。您甚至可以存储一些预定义行为的集合。例如:

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

(some additional checks are needed here of course)

(当然需要一些额外的检查)

And last note: in most cases avoid inheritance

最后一点:在大多数情况下避免继承

#4


2  

You need to factor out an interface. It is, after all, a fairly common practice to always start with an interface, then provide an abstract class to supply some default implementations. If you have an interface, you can make the enum implement the interface.

您需要分解界面。毕竟,一个相当常见的做法是始终从一个接口开始,然后提供一个抽象类来提供一些默认实现。如果您有接口,则可以使枚举实现接口。

#1


42  

All enums implicity extend Enum. In Java, a class can extend at most one other class.

所有枚举都隐含着Enum。在Java中,一个类最多可以扩展一个其他类。

You can, however, have your enum class implement an interface.

但是,您可以让您的枚举类实现一个接口。

From this Java tutorial on Enum Types:

从这个关于枚举类型的Java教程:

Note: All enums implicitly extend java.lang.Enum. Because a class can only extend one parent (see Declaring Classes), the Java language does not support multiple inheritance of state (see Multiple Inheritance of State, Implementation, and Type), and therefore an enum cannot extend anything else.

注意:所有枚举都隐式扩展java.lang.Enum。由于类只能扩展一个父级(请参阅声明类),因此Java语言不支持多状态继承(请参阅状态,实现和类型的多重继承),因此枚举不能扩展其他任何内容。

Edit for Java 8:

编辑Java 8:

As of Java 8, an interface can include default methods. This allows you to include method implementations (but not state) in interfaces. Although the primary purpose of this capability is to allow evolution of public interfaces, you could use this to inherit a custom method defining a common behavior among multiple enum classes.

从Java 8开始,接口可以包含默认方法。这允许您在接口中包含方法实现(但不包括状态)。虽然此功能的主要目的是允许公共接口的演变,但您可以使用它来继承定义多个枚举类之间的公共行为的自定义方法。

However, this could be brittle. If a method with the same signature were later added to the java.lang.Enum class, it would override your default methods . (When a method is defined both in a class's superclass and interfaces, the class implementation always wins.)

但是,这可能很脆弱。如果稍后将具有相同签名的方法添加到java.lang.Enum类中,它将覆盖您的默认方法。 (当在类的超类和接口中定义方法时,类实现总是获胜。)

For example:

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


4  

Your problem seems a good candidate for the Command Pattern

您的问题似乎是命令模式的一个很好的候选人

It is a good practice to use an enum as a logical group of supported actions. IMO, having a single enum to group all supported actions will improve the readability of your code. With this in mind, the Token enum should contain all the supported action types

将枚举用作受支持操作的逻辑组是一种很好的做法。 IMO,只需一个enum就可以对所有支持的操作进行分组,这将提高代码的可读性。考虑到这一点,令牌枚举应包含所有支持的操作类型

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

}

Consider creating an interface Actor which defines an a method say act as shown below:

考虑创建一个接口Actor,它定义一个方法,如下所示:

public interface Actor
{
   public void act();
}

Instead of having a single Base class that does too may things, you can have one class per supported command for e.g.

您可以为每个支持的命令创建一个类,而不是单个基类也可以执行某些操作。

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
    }
}

Finally, there will be a driver code that will take in as input the action to be performed, initialize the appropriate Actor and execute the action by invoking the act() method.

最后,将有一个驱动程序代码将输入要执行的操作,初始化相应的Actor并通过调用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;
          .... 

      }


   }
}

Such a design will ensure that you can easily add new commands and the code remains modular.

这样的设计将确保您可以轻松添加新命令,并且代码保持模块化。

#3


3  

As other say here, You can't extend enum. From design perspective this solution looks like it's too tightly coupled. I would advise to use more dynamic approach for this. You can create some kind of behavior map:

正如其他人所说,你无法扩展枚举。从设计的角度来看,这个解决方案看起来太紧密了。我建议使用更动态的方法。您可以创建某种行为图:

Map<Token, Runnable> behaviors;

This map could be easily modified or replaced. You can even store some sets of those predefined behaviors. In example:

可以轻松修改或替换此地图。您甚至可以存储一些预定义行为的集合。例如:

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

(some additional checks are needed here of course)

(当然需要一些额外的检查)

And last note: in most cases avoid inheritance

最后一点:在大多数情况下避免继承

#4


2  

You need to factor out an interface. It is, after all, a fairly common practice to always start with an interface, then provide an abstract class to supply some default implementations. If you have an interface, you can make the enum implement the interface.

您需要分解界面。毕竟,一个相当常见的做法是始终从一个接口开始,然后提供一个抽象类来提供一些默认实现。如果您有接口,则可以使枚举实现接口。