Question

I have an abstract class (or an interface). In this class I want to define an enum, in order to force classes that extend this abstract class (or implement this interface) to declare an enum with the same name.

abstract class Operator {
    public abstract enum Symbol;
    public Symbol value;
}

class Binary extends Operator {
    public enum Symbol {
        pls,
        min,
        mul,
        div,
        //...
    }
}
class Unary extends Operator {
    public enum Symbol {
        sin,
        cos,
        tan,
        cot,
        //...
    }
}

Assume I can't know the values of sub classes enums. I want that every extending class had an enum with that name and its values. I want to use enums (especially because it's easy to switch enums)

Était-ce utile?

La solution

There's no way to do this thing and moreover, even if it were, how would you call the subclass' implementation? I mean, only method calls can be virtual, ie dispatched at runtime. Types can not, so without cheating with reflection (which throws away any type safety anyhow, so that you don't even need to subclass), you would not be able to call the overridden type (in fact, types can't be overridden).

Maybe you can still achive your objectives by using composition:

public abstract class Operator<T extends Enum<T>> {
  public final Class<T> symbol;
  public Operator(Class<T> symbol) { this.symbol = symbol; }
}

public enum BinarySymbol { PLS, MIN, MUL, DIV }

public class Binary extends Operator<BinarySymbol> {
  public Binary(Object operand1, Object operand2, BinarySymbol symbol) {
    super(symbol);
  }
}

Your base class Operator can dynamically read the enumerated values through reflection, via Class.getEnumConstants()

Autres conseils

You can't enforce it at compile time. One way would be to use reflection at runtime to see if the class has been implemented correctly. I don't suggest this, but it's possible.

What you can do is have enums that implement a common interface, and then utilize those interfaces:

public interface YourEnumInterface<E extends Enum<E> & YourEnumInterface<E>> {

    //methods that your enum should be implementing

}

The extension for the interface generic declaration is there to guarantee it is only called by enums that implement your interface

And then any enum you have to specify can implement it like so:

public enum MyEnum implements YourEnumInterface<MyEnum> {

    // enum constants
    TEST_VALUE;

    // implementation of interface methods

}

From there, you would simply work with the YourEnumInterface as an object, and you can pass enum values for them:

public void doSomething(YourEnumInterface enm) {
    //work with enm
}

//Elsewheres...
doSomething(MyEnum.TEST_VALUE);

It should be noted, that once you lower your enum down to the interface itself, you won't be able to change the constant you are working with (without casting, which can potentially be unsafe). This should really be used more or less for passing things to a destination that just works with the values (like a config enum, or internalization of strings, etc)

So in relevance to forcing a subclass to implement it:

public class YourSuperClass {
    public abstract YourEnumInterface symbol;
}

A runnable example

public class EnumMagic {

    public static void main(String[] args) {
        YourSubClass clazz = new YourSubClass();
        //prints the default value
        System.out.println(clazz.getEnum().getValue());
        //gets the value of a specified enum constant
        System.out.println(clazz.getEnum().SECOND_TEST.getValue());
    }

}

abstract class YourSuperClass {

    protected YourEnumInterface symbol;

    public abstract YourEnumInterface getEnum();

}

interface YourEnumInterface<E extends Enum<E> & YourEnumInterface<E>> {

    public int getValue();

}

class YourSubClass extends YourSuperClass {

    public enum MyEnum implements YourEnumInterface<MyEnum> {

        TEST_VALUE(1),
        SECOND_TEST(2);

        private final int val;

        private MyEnum(int example) {
            this.val = example;
        }

        public int getValue() {
            return this.val;
        }

    }

    public YourSubClass() {
        this.symbol = MyEnum.TEST_VALUE;
    }

    public MyEnum getEnum() {
        return MyEnum.TEST_VALUE;
    }

}

The output of this progam is:

1
2

And you can simply get the class' specified enum constant via getEnum, which they will return their own internal enum.

However, once downcasted to the super type, e.g. YourSuperClass intc = clazz;, then you will lose this ability to specify the enum itself. Depending on how you store your instances will determine whether or not this requires changes.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top