43

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.

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:

      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.


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

4 Answers 4

57

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

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

From this Java tutorial on Enum Types:

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.

Edit for 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.

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.)

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
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks. I read that one, but obviously did not catch everything. I'll look at the interface approach. Like your avatar btw :). And thanks @Aubin, - for the extra link. Haven't read the specks yet, - but looks like I should. It looks to be in a language I like.
In a similar situation in which I wanted to inherit non-trivial implementation from an abstract base class, I ended up with an enum as a instance member.
7

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

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:

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.

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 Comments

Good points (I guess - I've been at Java only for a couple of weeks). My issue with this approach for this concrete case is that I have a lot of act's typically 50+. Quite a few is a one-liner. That would mean 50+ files and 50+ classes, am I correct? Or I could off course only add a class for the ones having a more huge code base. Further one important point is re-use of the same base ...
You could add an act method to the enum and instead of switch(token) just call token.act();
The set of commands are at three places that have to be in sync: enum, implemented classes and the "driver". It is easy to implement a command in one place and forget another. I would probably spare the driver and put the instances of the implemented Actors into another constructor parameter of the enumerator.
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

2 Comments

Thank you. Yes, I looked at Map approach as per my previous Q. Tried it out but guess I missed something - not that I don't want to look into it further, but thought down the road... Should perhaps do it now.
Also - regarding "tightly coupled" and "avoid inheritance". I did a quick web search on "java avoid inheritance" - and came upon these Why avoid Java Inheritance “Extends” and Why extends is evil, - but I have to read it a couple of more times hopefully grasping the extent of the issue.
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 Comment

Thanks I'll work on that. I'm rather new to Java, - so that kind of input is really helpful.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.