views:

141

answers:

5

I am attempting to find a way to force Java to load/initialize an enumerated type (which is nested within a class that contains a static Map).

This is important to me because the enumerated type has a constructor that populates said map, and without an explicit way to initialize this enum, the map will remain empty. I have attempted to use Class.forName, but this does not seem to work.

I suppose I could create an instance of the enum (and store it in soem other collection or something), but I would like to know if there is an elegant way to do this.

+3  A: 

Can't you just put initialization of the map in the static initializer of the Enum type?

public enum SomeEnum
{
    Member1, Member2, Member3 ...

    private static Map<K, SomeEnum> map = ...;
    static 
    {
        ...populate map...
    }
    ...

EDIT: It appears that the issue was that the Enum member definitions need to come first. I guess I just glossed over this. I fixed the example.

Matt
This doesn't seem to solve the problem as all instances created within SomeEnum are static anyway. The problem still remains: how do I tell the classloader to initialize/load an enum? Once I solve this, all my static code for the Enum should execute as needed.
Ryan Delucchi
Then can you elaborate a bit more? What problem is this causing? How can you access the map before the Enum is initialized if the Enum does the initialization? Is the map NOT contained within the Enum?
Matt
correct. The map is not contained within the enum. And I can't place the map within the enum because Java does not permit an enum constructor to populate a static collection that is within an enum.
Ryan Delucchi
+1 There's a nice example in JLS 8.9, under `enum Color`" http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.9
trashgod
What is the purpose of the map, then? I have written enums with internal static maps before; I guess I would like to know what you are trying to accomplish.
Matt
trashgod: Peter Lawrey gave an answer that is similar to the example.
Ryan Delucchi
A: 

This thread http://stackoverflow.com/questions/3798083/3800434#3800434 touches on the topic.

Tony Ennis
+2  A: 

You can just reference something in the enum class. For example:

public class EnumTest {
  static final Map<String, MyEnum> map = new HashMap<String, MyEnum>();

  enum MyEnum {
    FOO, BAR, BAZ;

    MyEnum() {
      map.put(name(), this);
    }
  }
  static {
    // ensure MyEnum is initialized
    MyEnum.values();
  }

  public static void main(String[] argsa) {
    System.out.println(map.size());
  }
}
Laurence Gonsalves
That is the basic workaround that I am using. But since the return value from MyEnum.values() isn't actually used in the code, I am concerned that maybe it will be "optimized out". Then again, I've seen a similar trick used for ensuring a regular class is initialized so perhaps this is a decent way to fix the problem.
Ryan Delucchi
I'm fairly certain that it can't be completely optimized out precisely because of the fact that it can potentially cause a class initialization. Java leaves far less "undefined" than C or C++, for better or worse.
Laurence Gonsalves
+1  A: 

Seems like this is exactly why it is often recommended to use accessor methods instead of directly referencing members. Your problem is that the code allows access to the map before it is initialized. Block arbitrary access to the map, and hide it behind an accessor method that makes sure it is initialized.

import java.util.Map;
import java.util.HashMap;

public enum EnumTest {
  FOO, BAR, BAZ;

  private static Map<String,EnumTest> map = null;

  public synchronized static Map getMap() {
    if (map == null) {
      map = new HashMap<String,EnumTest>();
      for ( EnumTest e : EnumTest.values() ) {
        map.put( e.name(), e );
      }
    }

    return map;
  }

  public static void main(String[] args) {
    System.out.println( EnumTest.getMap().size() );
  }
}
Dave Costa
+1 for endorsing encapsulation here.
Ryan Delucchi
+3  A: 

A class is loaded when you reference a class. This works the same for all classes.

The problem you have is more likely to be that an Enum value is initialised before any static block. i.e. you cannot refer to something initialise in a static block in a constructor. (Generally initialising static content in a constructor is a BAD idea) You need to initialise the Map in the static block, not the constructor.

Try

import java.util.Map; 
import java.util.HashMap; 

public enum EnumTest { 
  FOO, BAR, BAZ; 

  private static final Map<String,EnumTest> map = new LinkedHashMap<String,EnumTest>(); 
  static { 
      for(EnumTest e : EnumTest.values())
        map.put(e.name(), e); 
  } 

  public static void main(String... args) { 
    System.out.println(EnumTest.map); 
  } 
}
Peter Lawrey
This one is looking like the best approach so far. I'll give it a shot.
Ryan Delucchi
Btw +1 to trashgod as he pointed out an example that uses this same approach.
Ryan Delucchi
It worked. And I agree with your assertion that "initializing static content in a constructor is a BAD idea". I went ahead with this approach.
Ryan Delucchi