17

is there a simple way to subclass a Java enum?

I ask this because I have like 10 of them that implement the same interface but they also have the same implementation for some methods so I would like to reuse the code by placing all the same implementations in middle object that extends Enum and it is also the superclass of all the others I need.

Maybe it is not so straightforward as I think?

Thank in advance

Jack
  • 131,802
  • 30
  • 241
  • 343

7 Answers7

20

You can't do it, since the language does not allow you. And for a good logical reason: subclassing an enum would only make sense if you could remove some enum values from the subclass, not add new ones. Otherwise you would break the Liskov Substitution Principle.

This in brief states that every instance of a subclass should be acceptable whenever an instance of a superclass is expected. If you add a new enum member in an enum subclass, that clearly can't be accepted by someone knowing only the super enum.

For more details and possible alternatives, see this earlier answer of mine.

In your concrete case, @Jason's suggestion may offer a good solution (+1 for him :-)

Update to @OrangeDog's comment

Good point, I was a bit sloppy above :-) Implementation-wise you are right. However, from the logical point of view an enum type is fully described by the set of its valid values. And generally, a proper subclass is a specialization of its superclass. In other words, the set of valid subclass instances is (should be) always a subset of the superclass instance set. (Every dog is an animal, but not every animal is a dog.)

Community
  • 1
  • 1
Péter Török
  • 114,404
  • 31
  • 268
  • 329
  • 4
    Not strictly true. An enum value is internally an instance of the enum "class". A value from SubEnum could be easily passed as a SuperEnum, and you could call all the methods of SuperEnum on it. You just wouldn't be able to get an instance of SuperEnum that compares equal to it (which is true of identity equality for all classes). – OrangeDog Jan 05 '11 at 14:25
  • "And for a good logical reason: subclassing an enum would only make sense if you could remove some enum values from the subclass, not add new ones. Otherwise you would break the Liskov Substitution Principle." That is wrong. Extending an enum would make perfect sence if for instance you created a parent enum with some implemented generic methods. – Johan Jan 14 '14 at 12:12
  • 2
    Note though that you *can* create anonymous subclasses of enums, as follows: `enum E { INSTANCE {} }`. `E.INSTANCE.getClass() == E.class` will evaluate to false. (But I realize that this is usually of little help.) – aioobe Feb 13 '14 at 10:04
  • @Johan, subclassing in general means specializing, in other words *restricting* the set of possible values in a type. Dog is a subtype of Animal, not the other way around. For enum types, adding new values obviously contradicts this concept. I know this sounds illogical at first (it did to me too), but think it through. – Péter Török Feb 17 '14 at 11:52
  • @Péter Török it depends on what you want to do and how you want to do it. Perhaps you are limited to Dog but want Animal and Animal is missing. Enum is very limited and you want to make your own super enum to use. So yes it would be an extended subclass as result but not used as it. – Johan Feb 17 '14 at 12:15
  • @Johan, a super enum, as the name suggests, should be a *superclass* rather than a subclass. Technically you *can* indeed implement it in a different way (as other comments note), just with the risk of running into various sorts of trouble down the line. – Péter Török Feb 21 '14 at 10:29
  • @aioobe personnally I already used that with an enum that described the fields of my bean, in calls like `field.getValueOf(bean)` – bernard paulus Mar 25 '14 at 17:15
11

I ask this because I have like 10 of them that implement the same interface but they also have the same implementation for some methods so I would like to reuse the code by placing all the same implementations in middle object that extends Enum and it is also the superclass of all the others I need.

How about using a static helper class?

interface Animal
{
    public void speak();
}

class AnimalHelper
{
    public static void speakHelper(Animal animal) {
        // common methods here
    }
}

enum Dog implements Animal { SCHNAUZER, LABRADOR, ST_BERNARD, DACHSHUND;
    @Override public void speak() {
        AnimalHelper.speakHelper(this);
    }
};

enum Bird implements Animal { OWL, FINCH, DUCK, GOOSE; }
    @Override public void speak() {
        AnimalHelper.speakHelper(this);
    }
};
Jason S
  • 184,598
  • 164
  • 608
  • 970
  • Yes. but in case each of the enums has all common attributes, The duplication in constructors and getters and setters is evident. If you coud have a look at my question: http://stackoverflow.com/questions/31473115/code-duplication-in-enums-inheriting-a-common-interface – abksrv Jul 18 '15 at 17:10
6

With Java 8 you can put the shared implementation in the interface using default methods.

public class DefaultMethodOnEnumInterface {

    public interface Greeter {

        default public void greet() {
            System.out.println("Hello, world!");
        }
    }

    public enum Greeters implements Greeter {
        A,
        B;
    }

    public static void main(String[] args) {
        Greeters.A.greet();
        Greeters.B.greet();
    }
}

If access to methods implemented by the Enum class is required, add the signature to the interface:

public interface Greeter {

    default public void greet() {
        System.out.println("Hello, world! This is " + name());
    }

    /**
     * @see Enum#name()
     */
    public String name(); // implemented by Enum
}
Martin
  • 2,573
  • 28
  • 22
2

Whilst there have been proposals for abstract enums in Java, the benefits are considered far too low to outweigh the costs. You'll have to stick with forwarding method calls.

Jason Baker
  • 192,085
  • 135
  • 376
  • 510
Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
2

Try using Enums from Apache commons package, where you can subclass Enums. Dont know if this will help you.

sri
  • 21
  • 1
0

The language does not allow you to do so, as Enums are designed for representing enumerated values efficiently, not for implementing code. Try using another pattern like a static helper (utility) class.

OrangeDog
  • 36,653
  • 12
  • 122
  • 207
  • 1
    "not for implementing code" -- Wrong! You can do lots of useful stuff in both the enum class and each of the enum instances. You just can't subclass enums. The Enum framework is designed to produce typesafe singletons. – Jason S Jan 05 '11 at 14:33
  • @Jason - "designed for", not "only lets you" – OrangeDog Jan 05 '11 at 14:35
  • 1
    Java's enums were designed pretty carefully to allow methods in enum classes and instances. – Jason S Jan 05 '11 at 15:49
  • @Jason - What I mean is that the designers' thought process was "we have classes that encapsulate methods, now we need enums to encapsulate enumerated constant values" rather than "we have classes that encapsulate methods, now we need enums to encapsulate methods" – OrangeDog Jan 05 '11 at 16:04
  • Ah, got it. in other words enums were designed first and foremost to be safe singletons that encapsulate enumerated constant values, and while they were at it, they put some thought into it to give them some good object-oriented features. – Jason S Jan 05 '11 at 18:59
0

@Jason S has offered a good reply, but the static method make you lose the OOP's potentialities.

What about delegation? I mean:

  1. Define a common interface "I_A" where you defines all getters/setters and all other methods.

  2. Define a "struct-like" class "S_C" : it implements only the getters and setters, other methods are empty because are useless.

  3. To make your enumerations shorter (in terms of lines of code), define a second interface "I_A_Delegating" that extends the first ones, has an additional getter/setter of type "I_A" (it's our delegator) and, thanks to Java's default methods, define the getter/setter as calling the delegator's getter/setter.

  4. All of your enumerations implements "I_A_Delegating" and has a local instance of "S_C"

Code example:

public class EnumFakeExtension{
     /**"I_A"*/
     public static interface CommonInterface{
           public Object getFieldOne();
           public Object getFieldTwo();

           public void setFieldOne(Object o);
           public void setFieldTwo(Object o);

           public void someMethod();
     }

     /*"S_C"*/
     public static class CommonDelegator_FieldKeeper implements CommonInterface{
         Object oOne, oTwo;
           public Object getFieldOne(){ return oOne; }
           public Object getFieldTwo(){ return oTwo; }

           public void setFieldOne(Object o){ oOne = o; }
           public void setFieldTwo(Object o){ oTwo = o; }

           public void someMethod(){ /*empty*/ }
     }


     /**"I_A_Delegating"*/
     public static interface CommonInterface_Delegating extends CommonInterface{
         public CommonInterface getDelegate();
         public void setDelegate(CommonInterface delegator);

         /**Just to simplify*/
         public default void setDefaultDelegate(CommonInterface delegator){
             setDelegate( new CommonDelegator_FieldKeeper() );
         }


         public default Object getFieldOne(){ return getDelegate().getFieldOne(); }
         public default Object getFieldTwo(){ return getDelegate().getFieldTwo(); }

         public default void setFieldOne(Object o){ getDelegate().setFieldOne(o); }
         public default void setFieldTwo(Object o){ getDelegate().setFieldTwo(o); }
     }


    /*the enums, now*/

    public static enum EnumFirst implements CommonInterface_Delegating{
        FieldA, FieldB, FieldC;

        EnumFirst (){
            setDefaultDelegate();
        }
        final CommonDelegator_FieldKeeper delegator;

        public CommonInterface getDelegate(){ return delegator; }
        public void setDelegate(CommonInterface delegator){ this.delegator=delegator; }


        public void someMethod(){
            /*do what You need*/
        }
    }


    public static enum EnumSecond implements CommonInterface_Delegating{
        FieldA, FieldB, FieldC;

        EnumSecond (){
            setDefaultDelegate();
        }
        final CommonDelegator_FieldKeeper delegator;

        public CommonInterface getDelegate(){ return delegator; }
        public void setDelegate(CommonInterface delegator){ this.delegator=delegator; }

        public void someMethod(){
            /*do what You need, again*/
        }
    }
}
Marco Ottina
  • 399
  • 5
  • 12