Alex is working as a software developer since 1997. He coded many languages and started coding Java in 2000. As a person that likes to share his experience with others he started his professional blog http://alexr.blog.com Alexander has posted 5 posts at DZone. View Full User Profile

Enum Tricks: Dynamic Enums

10.19.2010
| 52773 views |
  • submit to reddit

Introduced to Java 1.5, Enum is a very useful and well known feature. There are a lot of tutorials that explain enums usage in details (e.g. the official Sun’s tutorial). Java enums by definition are immutable and must be defined in code. In this article I would like to explain use case when dynamic enums are needed and how to implement them.

Motivation

 There are 3 ways to use enums:

  1. direct access using the enum value, e.g. Color.RED
  2. access using enum name, e.g. Color.valueOf("RED")
  3. get enumeration of all enum values using values() method, e.g. Color.values()

Sometimes it is very convenient to store some information about enum in DB, file etc. In this case the enum defined in code must contain appropriate values.
For example let’s discover the enum Color:

enum Color {RED, GREEN, BLUE;}

Let’s assume that we would like to allow user to create his custom colors. So, we have to handle the table “Color” in database. But in this case we cannot continue using enum Color: if user adds new color (e.g. YELLOW) to DB we have to modify code and add this color to enum too.

OK, we can refuse to use enum in this case. Just rename enum to class and initialize list of colors from DB. But what if we already have 100 thousand lines of code where methods values() and valueOf() of enum Color are used? No problem: we can implement valueOf() and values() manually for the new class “Color.”

Now we see the problem. What if we have to refactor 12 enums like Color? Copy/Paste the new methods to all these classes? I would like to suggest other, more generic approach.

Making enums dynamic

Enum is compile time feature. When we create enum Foo, class Foo that extends java.lang.Enum is generated for us automatically. This is the reason that enum cannot extend other class (multiple inheritance is not supported in Java). Moreover some compiler magic prevents any attempt to manually write a class that extends java.lang.Enum.

The only solution is to write yet another class similar to Enum. I wrote class DynaEnum. This class mimics functionality of Enum but it is regular class, so it can be inherited. A static hash table holds elements of all created dynamic enums.

My DynaEnum contains a generic implementation of valueOf() and values() done using reflection, so users of this class do not have to implement them.

This class allows relatively easy refactoring for use case described above. Just change enum to class, inherit it from DynaEnum and write code to initialize members.

Example

Source code of the examples can be found here.

I implemented two examples: DigitsDynaEnum that does not have any added value relative to enum. It is implemented mostly to check that the base class functionality works. DigitsDynaEnumTest contains several unit tests.

package com.alexr.dynaenum;

public class DigitsDynaEnum extends DynaEnum<DigitsDynaEnum> {
	public final static DigitsDynaEnum ZERO = new DigitsDynaEnum("ZERO", 0);
	public final static DigitsDynaEnum ONE = new DigitsDynaEnum("ONE", 1);
	public final static DigitsDynaEnum TWO = new DigitsDynaEnum("TWO", 2);
	public final static DigitsDynaEnum THREE = new DigitsDynaEnum("THREE", 3);
	
	
	protected DigitsDynaEnum(String name, int ordinal) {
		super(name, ordinal);
	}

    public static <E> DynaEnum<? extends DynaEnum<?>>[] values() {
    	return values(DigitsDynaEnum.class);
    }
	
}

PropertiesDynaEnum is a dynamic enum that reads its members from properties file. Its subclass, WritersDynaEnum, reads a list of famous writers from a properties file WritersDynaEnum.properties.

package com.alexr.dynaenum;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;


public class PropertiesDynaEnum extends DynaEnum<PropertiesDynaEnum> {
	protected PropertiesDynaEnum(String name, int ordinal) {
		super(name, ordinal);
	}

    public static <E> DynaEnum<? extends DynaEnum<?>>[] values() {
    	return values(PropertiesDynaEnum.class);
    }
    
    protected static <E> void init(Class<E> clazz) {
    	try {
			initProps(clazz);
		}
		catch (Exception e) {
			throw new IllegalStateException(e);
		}
    }
    
    private static <E> void initProps(Class<E> clazz) throws Exception {
    	String rcName = clazz.getName().replace('.', '/') + ".properties";
    	BufferedReader reader = new BufferedReader(new InputStreamReader(Thread.currentThread().getContextClassLoader().getResourceAsStream(rcName)));
    	
    	Constructor<E> minimalConstructor = getConstructor(clazz, new Class[] {String.class, int.class});
    	Constructor<E> additionalConstructor = getConstructor(clazz, new Class[] {String.class, int.class, String.class});
    	int ordinal = 0;
    	for (String line = reader.readLine();  line != null;  line = reader.readLine()) {
    		line = line.replaceFirst("#.*", "").trim();
    		if (line.equals("")) {
    			continue;
    		}
    		String[] parts = line.split("\\s*=\\s*");
    		if (parts.length == 1 || additionalConstructor == null) {
    			minimalConstructor.newInstance(parts[0], ordinal);
    		} else {
    			additionalConstructor.newInstance(parts[0], ordinal, parts[1]);
    		}
    	}
    }
    
    @SuppressWarnings("unchecked")
	private static <E> Constructor<E> getConstructor(Class<E> clazz, Class<?>[] argTypes) {
    	for(Class<?> c = clazz;  c != null;  c = c.getSuperclass()) {
        	try {
        		return (Constructor<E>)c.getDeclaredConstructor(String.class, int.class, String.class);
        	} catch(Exception e) {
        		continue;
        	}
    	}
    	return null;
    }
}

The goal is implemented! We have class that mimics functionality of enum but is absolutely dynamic. We can change list of writers without re-compiling the code. Therefore we can actually store the enum values everywhere and change “enums” at runtime.

It is not a problem to implement something like “JdbcDynaEnum” that reads values from database but this implementation is out of scope of this article.

Limitations

The solution is not ideal. It uses static initialization that could cause problems in multi-class loaders environment. Members of dynamic enums obviously cannot be accessed directly (Color.RED) but only using valueOf() or values(). But still in some cases this technique may be very useful.

Conclusions

Java enum is a very powerful feature but it has serious limitations. Enum values are static and have to be defined in code. This article suggests trick that allows to mimic enum functionality and store “enum” values separately from code.

Legacy
Article Resources: 
Published at DZone with permission of its author, Alexander Radzin.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Lars Tore Hasle replied on Tue, 2010/10/19 - 3:59am

Nice article, however there seams to be a problem with your zip file

Greg Allen replied on Tue, 2010/10/19 - 5:41am

Hmm, isn't DynaEnum just

 DynaEnum<X> extends Map<X, Integer>

without removal? You could implement it with LinkedHashMap

Josh Marotti replied on Tue, 2010/10/19 - 10:05am

Dynamic enum is.... A CLASS!

Maybe it is just me, because I came from using C structs to C++ to Java that I've seen the evolution of structures, enums, and classes, but you are making an enum a class, here.  And that class is a map or a collection of some type.

 

Enums are meant for static values.  If you don't have static values, you don't have an enum.  Simple as that.  What do you 'get' by making a dynamic enum?


Don't get me wrong, there is a time and a place for enums, and I've used them heavily.  Hell, I've even had a visitor pattern implemented with enums... but a dynamic enum just seems silly to me.  Perhaps you can give me an example on how this 'dynamic enum' gives you something that, say, a dynamically loaded or wrapped map cannot?

Andrew McVeigh replied on Tue, 2010/10/19 - 10:10am in response to: Josh Marotti

Enums are meant for static values.  If you don't have static values, you don't have an enum.  Simple as that.  What do you 'get' by making a dynamic enum?

 

actually, the subject of extensible enums has a long, illustrious and fairly contentious history. Niklaus Wirth explicitly excluded enums from the Oberon language (the one after Modula2) because it is difficult to extend the behaviour of them and cater for all cases in existing code when extending code.  this is one of the drawbacks of enums -- they tend to be used in if and switch statements.

as a use case though, consider you might want to create an extension to your program and add a new file format which is handled.  you will then want to add to the enum to add a new value for this.

Chris Knoll replied on Tue, 2010/10/19 - 12:22pm

I agree with Josh. Calling this a 'DynamicEnum' is almost an oxymoron. The point of enumerations are that they are static and 'well known' almost like an interface.  Defining things in enums means that eveyrone who uses it knows what the valid items in the enums are (example, having an enum for the different type of transaction modes on a DB connection).  Making it dynamic means you don't know what's in the enum (it could change at any time) and therefore, isn't an enum anymore.

So, sorry, but while your code looks interesting, it completely misses the point of Enums.

 

Looking forward to seeing this added to the JCP in JDK 1.8.

 

-Chris

 

Josh Marotti replied on Tue, 2010/10/19 - 3:05pm in response to: Andrew McVeigh

That was why I applied a visitor pattern to an enum before ;)

Alexander Radzin replied on Sun, 2010/10/24 - 5:05pm in response to: Josh Marotti

Josh,Visitor pattern adds behavior to existing enum element. I tried to show that enum itself may be "dynamic", i.e. you can create enum and fetch its elements from file or DB. This is the point.

Alexander Radzin replied on Sun, 2010/10/24 - 5:12pm in response to: Josh Marotti

I believe that my "dynamic enum" and "dynamically loaded map" are pretty the same things. The problem is in refactoring. Probably I was not clear enough, but I tried to explain the usecase. Imagine that you defined your enum and used it across your application hundreds of times. Now you get new requirement that forces you to store the enum elements in database. You can change your code and move from enum to regular class that is just like enum but is actually not. Instnces of this class are initiated from DB. I tried to suggest a way to make this refactoring easier and more elegant. That's it.

Marcelo Magalhães replied on Tue, 2011/06/14 - 2:13pm

Excellent article. Exactly what I was looking, but the zip file it is corrupetd. Can you re-load it again in DZone?

Marcus Bond replied on Tue, 2011/12/20 - 8:22am

Interesting but really this article should be called 'List of Values tricks' and only serves to confuse the meaning and purpose of enums which are not supposed to be dynamic. The key word below is compiler.

Enum helps because rather than say enumerating a traffic light system Red, Amber, Green with integers (but nothing prevents you setting a light to a different arbitrary integer outside of the permitted range without extra validation code) you can create an Enum class (TrafficLightColourEnum) and the compiler will ensure that only TrafficLightColourEnum instances are used, furthermore these can (and probably would) be used in switch statements. Loading up TrafficLightColour values (say Pink, Blue, Purple, Grey) dynamically would be useless.

A custom unique colour code list along with description applied to say email categories would make sense to be dynamic but it is a set / list of values and your code elsewhere doesn't rely on or care about the values, and importantly neither does the compiler.

So, view Enum as something defined at compile time and fixed, if it is dynamic then you have a set, the two are similar but different things where an Enum is effectively a fixed set (not a java.util.Set) that the compiler is aware of.

As an aside, your DigitsDynaEnum having static references to the allowed instances is similar to a trick used to implement enum-esque behaviour (but without switch-case support) in Java prior to the introduction of Enum in 1.5, just remove the Generics and initialise them all via a private constructor in a static block.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.