13

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.

Ryan Delucchi
  • 7,718
  • 13
  • 48
  • 60
  • 2
    If you're not guaranteed the enum will be constructed before you need to use the map, you might want to reconsider your design here, though Matt's recommendation of using a static initializer will probably get you what you want. – Bryan Sep 29 '10 at 00:48
  • 1
    `Class.forName` does initialize a class. If it "doens't work", you code has other problems you don't realize. Why not post your code. – irreputable Sep 29 '10 at 06:17
  • Basically I see an exception stating that the class is "invalid", and I know for sure I have the correct package path. is Class.forName() *supposed* to work on enums in all cases? – Ryan Delucchi Sep 29 '10 at 07:27
  • This thread http://stackoverflow.com/questions/3798083/3800434#3800434 touches on the topic. – Tony Ennis Sep 29 '10 at 01:22

4 Answers4

10

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
  • 525,659
  • 79
  • 751
  • 1,130
  • This one is looking like the best approach so far. I'll give it a shot. – Ryan Delucchi Sep 29 '10 at 07:29
  • 1
    Btw +1 to trashgod as he pointed out an example that uses this same approach. – Ryan Delucchi Sep 29 '10 at 07:29
  • 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 Sep 29 '10 at 07:58
  • Probably I'm going to use ImmutableMap in Guava or Collections.unmodifiableMap to prevent any potential item addition in map field which is not actually defined in EnumTest. – wonhee Feb 06 '15 at 21:39
5

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 H
  • 7,311
  • 5
  • 45
  • 54
  • 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 Sep 29 '10 at 00:47
  • 1
    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 H Sep 29 '10 at 00:49
  • 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 Sep 29 '10 at 01:08
  • 2
    +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 Sep 29 '10 at 01:20
  • 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 H Sep 29 '10 at 05:30
  • trashgod: Peter Lawrey gave an answer that is similar to the example. – Ryan Delucchi Sep 29 '10 at 07:31
4

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
  • 137,896
  • 35
  • 246
  • 299
  • 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 Sep 29 '10 at 07:26
  • 2
    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 Sep 30 '10 at 01:02
2

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
  • 47,262
  • 8
  • 56
  • 72