How to Define a Class Constant Inside an Enum

Is it possible to define a class constant inside an Enum?

This is advanced behavior which will not be needed in 90+% of the enumerations created.

According to the docs:

The rules for what is allowed are as follows: _sunder_ names (starting and ending with a single underscore) are reserved by enum and cannot be used; all other attributes defined within an enumeration will become members of this enumeration, with the exception of __dunder__ names and descriptors (methods are also descriptors).

So if you want a class constant you have several choices:

  • create it in __init__
  • add it after the class has been created
  • use a mixin
  • create your own descriptor

Creating the constant in __init__ and adding it after the class has been created both suffer from not having all the class info gathered in one place.

Mixins can certainly be used when appropriate (see dnozay's answer for a good example), but that case can also be simplified by having a base Enum class with the actual constants built in.

First, the constant that will be used in the examples below:

class Constant:  # use Constant(object) if in Python 2
def __init__(self, value):
self.value = value
def __get__(self, *args):
return self.value
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.value)

And the single-use Enum example:

from enum import Enum

class Planet(Enum):
MERCURY = (3.303e+23, 2.4397e6)
VENUS = (4.869e+24, 6.0518e6)
EARTH = (5.976e+24, 6.37814e6)
MARS = (6.421e+23, 3.3972e6)
JUPITER = (1.9e+27, 7.1492e7)
SATURN = (5.688e+26, 6.0268e7)
URANUS = (8.686e+25, 2.5559e7)
NEPTUNE = (1.024e+26, 2.4746e7)

# universal gravitational constant
G = Constant(6.67300E-11)

def __init__(self, mass, radius):
self.mass = mass # in kilograms
self.radius = radius # in meters
@property
def surface_gravity(self):
return self.G * self.mass / (self.radius * self.radius)

print(Planet.__dict__['G']) # Constant(6.673e-11)
print(Planet.G) # 6.673e-11
print(Planet.NEPTUNE.G) # 6.673e-11
print(Planet.SATURN.surface_gravity) # 10.44978014597121

And, finally, the multi-use Enum example:

from enum import Enum

class AstronomicalObject(Enum):

# universal gravitational constant
G = Constant(6.67300E-11)

def __init__(self, mass, radius):
self.mass = mass
self.radius = radius
@property
def surface_gravity(self):
return self.G * self.mass / (self.radius * self.radius)

class Planet(AstronomicalObject):
MERCURY = (3.303e+23, 2.4397e6)
VENUS = (4.869e+24, 6.0518e6)
EARTH = (5.976e+24, 6.37814e6)
MARS = (6.421e+23, 3.3972e6)
JUPITER = (1.9e+27, 7.1492e7)
SATURN = (5.688e+26, 6.0268e7)
URANUS = (8.686e+25, 2.5559e7)
NEPTUNE = (1.024e+26, 2.4746e7)

class Asteroid(AstronomicalObject):
CERES = (9.4e+20 , 4.75e+5)
PALLAS = (2.068e+20, 2.72e+5)
JUNOS = (2.82e+19, 2.29e+5)
VESTA = (2.632e+20 ,2.62e+5

Planet.MERCURY.surface_gravity # 3.7030267229659395
Asteroid.CERES.surface_gravity # 0.27801085872576176

Note:

The Constant G really isn't. One could rebind G to something else:

Planet.G = 1

If you really need it to be constant (aka not rebindable), then use the new aenum library [1] which will block attempts to reassign constants as well as Enum members.


1 Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.

How to define static constants in a Java enum?

As IntelliJ IDEA suggest when extracting constant - make static nested class. This approach works:

@RequiredArgsConstructor
public enum MyEnum {
BAR1(Constants.BAR_VALUE),
FOO("Foo"),
BAR2(Constants.BAR_VALUE),
...,
BARn(Constants.BAR_VALUE);

@Getter
private final String value;

private static class Constants {
public static final String BAR_VALUE = "BAR";
}
}

Declare constants inside of enum

I would use an enum for the Categories as well. If you place this in a top level class, it will look natural.

You have to define your enum values before any other constants.

AFAIK This is done to simplify the syntax. You have the enum values first with a special, simplified syntax, followed by the constants you define. The need for this is more obvious when you start overriding methods in your constants.

Using Enum to store constants and use those constants in multiple classes

As commented, a constant that changes value is not a constant.

While you certainly can store changing state on an enum, you should ask yourself if doing so is a good idea. Generally not, in my opinion. Enum objects are usually used as constants, hence the convention of naming in all uppercase.

Map

You can accomplish the goal of having type-safe limited set of objects (enums) along with more obviously changing state by using a Map. A map would pair each enum object with an object of a custom type to contain your changing state.

EnumMap

The EnumMap class is an implementation of Map that is highly efficient, taking little memory and little CPU.

Here is some untested code as an example.

enum TravSalesAspect NODE_COUNT, SEQUENCE_LENGTH, POPULATION_SIZE, MAX_EDGE_WEIGHT, MUTATION_RATE ;
record AlgorithmState ( int val , boolean alreadySet ) {}

final Map< TravSalesAspect , AlgorithmState > map = new EnumMap<>() ;

Our map is empty. So we want to initialize with some default values.

We access an array of all the enum objects. From that array, we make a stream. For each element in the stream, we put a key-value entry into the map.

Arrays.stream( TravSalesAspect.values() ).forEach( aspect -> map.put( aspect , new AlgorithmState( 0 , false ) ) ) ;  // Initializing the map.

When you want to later set the state:

if( ! map.get( someAspect ).alreadySet() ) 
{
map.put( someAspect , new AlgorithmState( 42 , true ) ) ;
}

By the way, note that in Java 16+ both enum and record can be defined locally if you so desire.

Of course the code shown here is not thread-safe; beware.

Use final constant inside an enum

I suggest this :

enum KeyTypes {

BOLSA(), // default constructor

LLAVE(), // default constructor

MAGIC("net.labs.key.magical");

private static final String NONE = "";

private final String keyClass;

KeyTypes() {
this(NONE);
}

KeyTypes(String keyClass).....

}

Use Enum for keeping Constants

It is recommended to use Enum. The possible reasons are like

  • provides default functions to iterate through constants
  • Can write static functions to get the values based on key

It is possible to make the methods by yourself, to make your class self sustained. but the preferable approach is using Enum

Is an enum constant-specific class body static or non-static?

Well this is a strange case.

It appears that the problem is:

  • In this case, the private member should be accessible (6.6.1.):

    Otherwise, the member or constructor is declared private, and access is permitted if and only if it occurs within the body of the top level class that encloses the declaration of the member or constructor.

  • However, private members are not inherited (8.2):

    Members of a class that are declared private are not inherited by subclasses of that class.

  • Therefore, printMe is not a member of the anonymous subclass and the compiler searches for it within the superclass* Operation (15.12.1):

    If there is an enclosing type declaration of which that method is a member, let T be the innermost such type declaration. The class or interface to search is T.

    This search policy is called the "comb rule". It effectively looks for methods in a nested class's superclass hierarchy before looking for methods in an enclosing class and its superclass hierarchy.

  • And here is where it gets strange. Because printMe is found in a class that also encloses PLUS, the object that the method is called on is instead determined to be an enclosing instance of Operation, which doesn't exist (15.12.4.1):

    Otherwise, let T be the enclosing type declaration of which the method is a member, and let n be an integer such that T is the n'th lexically enclosing type declaration of the class whose declaration immediately contains the method invocation. The target reference is the n'th lexically enclosing instance of this.

    It is a compile-time error if the n'th lexically enclosing instance of this does not exist.

So in short, because printMe is only a member of Operation (and not inherited), the compiler is compelled to invoke printMe on a non-existent outer instance.

However, the method is still accessible and we can find it by qualifying the invocation:

@Override
double apply(double x, double y) {
// now the superclass is searched
// but the target reference is definitely 'this'
// vvvvvv
super.printMe(x);
return x + y;
}

The two error messages sound contradictory to each other [...].

Yes, this is a confusing aspect of the language. On the one hand, an anonymous class is never static (15.9.5), on the other hand, an anonymous class expression can appear in a static context and therefore has no enclosing instance (8.1.3).

An anonymous class is always an inner class; it is never static.

An instance of an inner class I whose declaration occurs in a static context has no lexically enclosing instances.

To help understand how this works, here is a formatted example:


class Example {
public static void main(String... args) {
new Object() {
int i;
void m() {}
}
;
}

}

Everything in italics is the static context. The anonymous class derived from the expression in bold is considered inner and non-static (but has no enclosing instance of Example).

Since the anonymous class is non-static it cannot declare static non-constant members, despite that it is itself declared within a static context.


* Besides obscuring the matter a little, the fact that Operation is an enum is completely irrelevant (8.9.1):

The optional class body of an enum constant implicitly defines an anonymous class declaration that extends the immediately enclosing enum type. The class body is governed by the usual rules of anonymous classes [...].



Related Topics



Leave a reply



Submit