<The previous article in this series | The table of contents of this series | The next article in this series>
To Know Where to Use Enums and Where to Use Constants Groups
About: Java Programming Language
Ah, we can't extend Enums. That is so . . .
Is that a problem?
For my plan, yes. I thought, I would create a base Enum, 'Test1BaseEnum', and extended Enums, 'Test1ExtendedEnum1' and 'Test1ExtendedEnum2' like this.
public enum Test1BaseEnum {
Value1 ("Value1"),
Value2 ("Value2");
String value = null;
Test1BaseEnum (String p_value) {
value = p_value;
}
public String getValue () {
return value;
}
}
public enum Test1ExtendedEnum1 extends Test1BaseEnum {
Value3 ("Value3"),
Value4 ("Value4");
Test1ExtendedEnum1 (String p_value) {
super (p_value)
}
}
public enum Test1ExtendedEnum2 extends Test1BaseEnum {
Value3 ("Value3"),
Value5 ("Value5");
Test1ExtendedEnum2 (String p_value) {
super (p_value)
}
}
However, that doesn't work.
Why?
Inheritance of Enums is prohibited.
I guess, there is a reason why it is prohibited. They didn't prohibit it from ill will or stinginess, did they?
It seems that the keyword 'enum' means that the class inherits the Enum class. As multiple inheritance isn't allowed in Java, we can't specify another super class.
That is a matter of the enum specification of Java. What if you create your own enum classes?
Well, let's think like this.
public class Test2BaseEnum {
String value = null;
public static final Test2BaseEnum Value1 = new Test2BaseEnum ("Value1");
public static final Test2BaseEnum Value2 = new Test2BaseEnum ("Value2");
Test2BaseEnum (String p_value) {
value = p_value;
}
public String getValue () {
return value;
}
}
public final class Test2ExtendedEnum1 extends Test2BaseEnum {
public static final Test2ExtendedEnum1 Value3 = new Test2ExtendedEnum1 ("Value3");
public static final Test2ExtendedEnum1 Value4 = new Test2ExtendedEnum1 ("Value4");
private Test2ExtendedEnum1 (String p_value) {
super (p_value);
}
}
public final class Test2ExtendedEnum2 extends Test2BaseEnum {
public static final Test2ExtendedEnum2 Value3 = new Test2ExtendedEnum2 ("Value3");
public static final Test2ExtendedEnum2 Value5 = new Test2ExtendedEnum2 ("Value5");
private Test2ExtendedEnum2 (String p_value) {
super (p_value);
}
}
That can be compiled successfully. . . . Well, but, I understand now why inheritance isn't allowed for Enums. That code doesn't work.
How doesn't that work?
Let's suppose we receive a value of 'Test2ExtendedEnum1' as an argument of a method like this.
public static void testTest2 (Test2ExtendedEnum1 p_enumValue) {
System.out.println (p_enumValue.getValue ());
}
We can't pass 'Test2ExtendedEnum1.Value1' into the method because 'Test2ExtendedEnum1.Value1' is an instance of 'Test2BaseEnum', not of 'Test2ExtendedEnum1' or any sub class of it.
So, that is the reason why we can't extend Enums.
Although we will be able to pass 'Test2ExtendedEnum1.Value1' into the method if we make the argument type 'Test2BaseEnum', but . . .
. . . that will be meaningless.
The essence of enums is to restrict an argument of a method to a set of predetermined values, in this case, values contained in 'Test2ExtendedEnum1'. If we make the argument type 'Test2BaseEnum', we will be able to pass 'Test2ExtendedEnum2.Value5', which is what we need to prohibit.
That restriction is the exact reason why we intend to use enums.
If we just want to list some constants without that necessity, an interface with member variables will do, like this.
interface Test3BaseConstantsGroup {
String Value1 = "Value1";
String Value2 = "Value2";
}
interface Test3ExtendedConstantsGroup1 extends Test3BaseConstantsGroup {
String Value3 = "Value3";
String Value4 = "Value4";
}
interface Test3ExtendedConstantsGroup2 extends Test3BaseConstantsGroup {
String Value3 = "Value3";
String Value5 = "Value5";
}
That way, we can extend constants groups as we like, and there is no necessity to call the method 'getValue'.
However, our test method will have to be like this.
public static void testTest3 (String p_constant) {
System.out.println (p_constant);
}
We will be able to pass any String instance.
Of course, we can check passed values in the method at run time, but that way, we can find out violations only at run time by doing intensive tests.
We may be able to find out them by scanning source files, but anyway, that would be cumbersome.
With enums, the compiler just detects violations, which is a huge difference.
And IDEs may give us some help, if we define an argument of a method as an enum.
Yes. IDEs may detect and recommend us what we have to pass into the argument, which is a big help.
And there is another benefit of enums: we can enumerate defined values.
Ah, . . . but about that, I think, I can implement such a function also into constants groups.
Isn't there a solution in which we can restrict passed values at compile time and also can maintain a hierarchy of allowed values groups?
Do you have any idea?
Well, . . . how about this?
public class Test4BaseEnum {
public static final Test4BaseEnumValue Value1 = new Test4BaseEnumValue ("Value1");
public static final Test4BaseEnumValue Value2 = new Test4BaseEnumValue ("Value2");
public static class Test4BaseEnumValue extends Test4ExtendedEnum1.Test4ExtendedEnum1Value {
Test4BaseEnumValue (String p_value) {
super (p_value);
}
}
}
public class Test4ExtendedEnum1 extends Test4BaseEnum {
public static final Test4ExtendedEnum1Value Value3 = new Test4ExtendedEnum1Value ("Value3");
public static final Test4ExtendedEnum1Value Value4 = new Test4ExtendedEnum1Value ("Value4");
public static class Test4ExtendedEnum1Value {
String value = null;
Test4ExtendedEnum1Value (String p_value) {
value = p_value;
}
public String getValue () {
return value;
}
}
}
public static void testTest4 (Test4ExtendedEnum1.Test4ExtendedEnum1Value p_enumValue) {
System.out.println (p_enumValue.getValue ());
}
Ah, you created value classes hierarchy in the reverse direction, but . . . that doesn't work, as you can see easily.
Can I see easily? . . . Well, how?
Just try to create 'Test4ExtendedEnum2' too.
Well, . . . I see. 'Test4BaseEnumValue' can't extend two classes. . . . Then, what if we use interfaces like this?
interface BaseEnumValueInterface {
String getValue ();
}
abstract public class BaseEnumValue implements BaseEnumValueInterface {
String value = null;
BaseEnumValue (String p_value) {
value = p_value;
}
@Override
public String getValue () {
return value;
}
}
public class Test5BaseEnum {
public static final Test5BaseEnumValue Value1 = new Test5BaseEnumValue ("Value1");
public static final Test5BaseEnumValue Value2 = new Test5BaseEnumValue ("Value2");
public interface Test5BaseEnumValueInterface extends Test5ExtendedEnum1.Test5ExtendedEnum1ValueInterface {
}
public static class Test5BaseEnumValue extends BaseEnumValue implements Test5BaseEnumValueInterface {
private Test5BaseEnumValue (String p_value) {
super (p_value);
}
}
}
public class Test5ExtendedEnum1 extends Test5BaseEnum {
public static final Test5ExtendedEnum1Value Value3 = new Test5ExtendedEnum1Value ("Value3");
public static final Test5ExtendedEnum1Value Value4 = new Test5ExtendedEnum1Value ("Value4");
public interface Test5ExtendedEnum1ValueInterface extends BaseEnumValueInterface {
}
public static final class Test5ExtendedEnum1Value extends BaseEnumValue implements Test5ExtendedEnum1ValueInterface {
private Test5ExtendedEnum1Value (String p_value) {
super (p_value);
}
}
}
public static void testTest5 (Test5ExtendedEnum1.Test5ExtendedEnum1ValueInterface p_enumValue) {
System.out.println (p_enumValue.getValue ());
}
Ah, that won't do, you know.
I know? . . . Well, I don't seem to know. In fact, that seems to work . . .
That may not give any error at compile time or run time, but does that realize the essence of enums we discussed above?
What do you mean?
Someone will be able to do like this.
testTest5 (new Test5ExtendedEnum1.Test5ExtendedEnum1ValueInterface () {public String getValue () {return "Well, your restriction is flawed . . .";}});
No! Don't do that! It's prohibited!
Being said "It's prohibited," it isn't prohibited on the code.
Please, don't do that, I ask . . .
If asking solves the problem, you could have just asked from the beginning.
Well, to think of it, we can't also expect help from IDEs for the code, for 'Test5ExtendedEnum1.Test5ExtendedEnum1ValueInterface' doesn't give options for values.
Fundamentally, the type of the argument of the test method has to be a final class for the restriction to work.
Otherwise, we can evade the restriction by creating an arbitrary class that extends the argument class or implements the argument interface.
In fact, 'test4' was flawed in that meaning, even if multiple inheritance is allowed in Java.
So, as a conclusion, we can't use values of any super class or values of any sub class: we have to define values as instances of the argument class itself. That seems to mean that we can't create extendable enums based on the present specifications of Java.
If we don't need to enforce any restriction of possible values, we can just use constants groups. If we need enumeration, we will implement the enumeration (we will do so in a future article). That way, we can build any hierarchy of those groups.
If we need to enforce a restriction of possible values, we can't help but to use enums. In that case, we will have to copy definitions of values to some enums if those values are shared among those enums. If we want to eliminate such crude copying of definitions, we can't do it inside Java, and will have to create a preprocessor to create Java Enums. It won't be difficult to create a preprocessor that creates Enum Java sources from a XML file that defines the hierarchy of those Enums.
<The previous article in this series | The table of contents of this series | The next article in this series>