1

I have a situation where I would like to used an instance of an object called Abstraction, which would be a Java interface looking something like:

public interface Abstraction {
   public enum Actions {
   }
}

The idea being that any class implementing Abstraction has to implement enum Actions (I realise this doesn't work).

A class implementing the interface may look like:

public class AbstractionA implements Abstraction {
   public enum Actions {
    f, c, r, a
   }
}

In another class I would want to create an Abstraction object like:

Abstraction abs = new AbstractionA();

and then be able to access the enum values applicable to the Abstraction object created, e.g.

abs.Action.r;

I realise my approach to this is all wrong but cannot see an appropriate way to handle this type of situation. How can I implement something like this where different implementations of the interface have a varying subset of the options I would generally want to put in an enum? Perhaps I can implement the enum with all possible options in the interface and then somehow restrict implementations of the interface to using a subset of those enum values?

EDIT: Another example implementation might be

public class AbstractionB implements Abstraction {
   public enum Actions {
    f, c, b, r, a
   }
}

I think I have figured out a way forward with this:

public interface Abstraction {
   public enum Actions {
    f, c, b, r, s, a  
   }
   public Actions[] availableActions();
}

Then implement with:

public class HunlAbstractionA implements Abstraction{
   @Override
   public Actions[] availableActions()
   {
    Actions[] actions = new Actions[] {Actions.f, Actions.c, Actions.r, Actions.a};
    return actions;
   }
}

This way I have access to all possible actions listed in the interfaces enum and can make checks to ensure an Action to be dealt with is one of the availableActions for the created class.

Steve W
  • 1,108
  • 3
  • 13
  • 35

2 Answers2

2

Recommendation

I'd recommend the following approach.

This approach uses a combination of generics and reflection to help explicitly indicate the need to implement or choose an appropriate enum, it also gives you the option of preserving information about the enum type whilst hiding all other information about the specific Abstraction implementation.

/**
 * An abstraction with an implementation-defined enum
 * @param <E> your custom enum.
 */
interface Abstraction<E extends Enum> {

    //this gives you the enum constants as a list
    Class<E> getEnumType();

}

class AbstractionA implements Abstraction<AbstractionA.EnumA> {
    enum EnumA {
        FOO,
        BAR
    }

    @Override
    public Class<EnumA> getEnumType() {
        return EnumA.class;
    }
}

class AbstractionB implements Abstraction<AbstractionB.EnumB> {
    enum EnumB {
        FOO,
        BAR
    }

    @Override
    public Class<EnumB> getEnumType() {
        return EnumB.class;
    }
}

Note that unfortunately we can supply a default implementation of getEnumType() due to type erasure.

Usage Example

class Main {
    public static void main(String[] args) {
        Abstraction myAbstractionA = new AbstractionA();
        Abstraction<AbstractionB.EnumB> myAbstractionB = new AbstractionB();

        Class enumAType = myAbstractionA.getEnumType();
        Class<AbstractionB.EnumB> enumBType = myAbstractionB.getEnumType();
        Object[] enumsA = enumAType.getEnumConstants();
        AbstractionB.EnumB[] enumsB = enumBType.getEnumConstants();
        System.out.printf("Enums of the same order are still non-identical: %s", enumsA[0].equals(enumsB[0]));
        System.out.println();

        Enum enumA = ((Enum)enumsA[0]);
        Enum enumB = ((Enum)enumsB[1]);
        System.out.printf("We can get enum constants in order, and get the orderinal of the enum: A=%s, B=%s", enumA.ordinal(), enumB.ordinal());
        System.out.println();

        enumA = Enum.valueOf(enumAType, "FOO");
        enumB = Enum.valueOf(enumBType, "BAR");
        System.out.printf("We can get enum constants by name and get the name out of the enum: A=%s, B=%s", enumA.name(), enumB.name());
        System.out.println();
    }
}

Alternatives

If you can use an abstract class instead of an interface, you may prefer a solution similar to this related answer.

Edit: If you have a common set of constants you want to share across your actions, you should probably use a global/shared enum for those constants and define only the extensions themselves in the custom Abstractions. If you cast them all to Enum and use .equals() as needed, this should work in most cases.

Background

As you have stated you know, it is not possible to place member objects (variable or classes) of an interface.

However, the good news is that java actually supports the behaviour you want pretty well.

There are 3 key features that relate to my recommendation:

Enums are Objects

Firstly, enums in java are fully-fledged Objects, which all extend java.lang.Enum, and all implement .equals().

So, you can store different any enum class' values in a variable of type java.lang.Enum and compare them with .equals().

And, if you want to pretend that values of different enum classes are the same because they share the same name (or are the nth constant in their respective class), you can do that too.

Note that this also means that custom enums can contain complex data and behaviour like any other class, beyond it's use as a unique identifier.

See the Enum API documentation for details.

Java Reflection

Secondly, Java has extensive reflection support. For our purposes, java.lang.Class has a method called getEnumConstants() for getting the enum constants (or null if the class is not an enum). See the Class API documentation for details.

Cyclic Dependancies

Thirdly, at least when it comes to generics, Java is permissive when it comes to cyclic dependancies, so you can define a generic interface depends on a specialisation of that generic. Java won't mind.

Cosmo
  • 144
  • 6
0

Interface is a contract that you want anyone to provide an implementation of that contract. In your example code you do not have a method but a definition of a enum called Action.

Generally enum is a set of constants hence we do not expect multiple classes to come up with different implementations of the same constant.

So you might want to rethink about your approach and figure out a better way. Hope this will help moving you in correct direction.

lkamal
  • 3,788
  • 1
  • 20
  • 34
  • Yeah, I said I know my approach was all wrong but used it to illustrate what I'm trying to do in order that somebody might be able to suggest a viable approach – Steve W May 19 '18 at 10:57
  • 2
    Though I agree with what you posted, I would consider this to be a comment, not an answer. – M. le Rutte May 19 '18 at 11:11
  • @SteveW Maybe we could discuss the question further to figure out what you expect from the implementation classes related to these enum Action. – lkamal May 19 '18 at 11:57
  • Sure, if you need me to add any detail to the question just let me know – Steve W May 19 '18 at 11:59
  • @SteveW We only have one implementation in question AbstractionA; maybe you could add another implementation and explain how different implementations will be used in your approach? – lkamal May 19 '18 at 12:10