+3  A: 

You can try the typesafe enum pattern described by Joshua Bloch in the first edition of his book "Effective Java".

The idea is to create a class with a private constructor. Inside this class you define static instances of any number you want to-just as in a Java enum.

I don't know if there is a limit for the number of static members in the Java language, though.

migu
That exactly seems to be so. The error states a limit of the static initializer code... not the enum itself. But I'll investigate this a little... thanks.
Daniel Bleisteiner
Then perhaps you have to create a fixed set of instances that are not static. However, that would make it impossible to use a constant like Suite.HEARTS directly.Your requirement seems to ask for quite an unusual solution.
migu
Thanks for your input here... I've come to a similar solution as described by that pattern.
Daniel Bleisteiner
+7  A: 

Simple. Don't use enum for this.

The chances are that your source code does not explicitly refer to many of the enum values. Rather, you are using the enum as a convenient way to map between unique object instances and string names. So just replace the enum type with a type that explicitly manages the mapping, initializing it by reading from a file or database. If you do it right, you will get the computational properties and type-safety of an enum. The only thing you lose is the syntactic sugar ... and the statics.

This approach will have the added advantage that you can modify the 'descriptors' mapping without modifying the source code of your program.

By the way, the limitation you are running into is imposed by the JVM class file format. A method or constructor has an upper size limit of 2^16 bytes, and a classes static initialization code is represented as a special method with a funky name.

Stephen C
I'm running into this because I want to do the exact opposite - pulling the rather static information of the descriptor into the code. It is currently a database table with roughly 1400 entries that I want to transfer into the code. The reason is more control over the data and easier handling in our large project. The topic is too complex to explain it short.Reading some kind of flat file with a singleton might be a way to go. Thanks for that idea.
Daniel Bleisteiner
A: 

This is not a simple solution, but you may try to... patch the Java compiler.

When you write an enum, the Java compiler generate a class which extends java.lang.Enum (possibly several classes, if there are constant-specific methods). The class has some (hidden) static fields, which, at the bytecode level, are initialized with the special <clinit>() method (which the JVM calls when the class is first used). As any other method, the code for the <clinit>() method is limited to 65535 bytes. Each constant contributes to about 20 to 22 bytes in the <clinit>() bytecode (more if there are constant-specific constructors), so you hit the limit at about 3000 enumeration constants.

Now the <clinit>() method has a funny name but it is nothing really special; it can invoke other methods. The Java compiler could split the mammoth <clinit>() into several hidden sub-methods which <clinit>() would then invoke one after the other. The Java compiler does not currently do that, but it theoretically could. The result would be processable by any JRE.

Alternatively, synthesize your enum class synthetically, generating bytecode from a dedicated program, possibly itself written in Java. In essence, this is like writing your own specialized compiler, for a specific target and using your own source syntax. The BCEL library may help.

Note that there are other limits which could jump on you. For each enumeration constant, the static code (the one in <clinit>()) uses two "constants", which are internal values aggregated in the "constant pool" part of the compiled class. The two values are the constant name (as a string) and the resulting static field reference. There is a hard limit on 65536 constant pool entries (indexes are on 16 bits), so no more than a bit more than 32000 enumeration constants. A patched Java compiler could walk around that limit by generating several hidden classes, each with its own constant pool. A harder limit is in the number of static fields: each enumeration constant becomes a static field in the "enum" class, and there can be no more than 65535 fields (static or not) in a class.

Thomas Pornin
I won't break compatibility of my java bytecode. That's no option.
Daniel Bleisteiner
I might have been unclear. The _whole point_ of my message is that it could be done while maintaining the bytecode fully compatible with what the JVM expects.
Thomas Pornin
A: 

You might try nesting static inner-classes within a top-level class

Everyone
The problem here is the number of necessary nestings to achieve the number of elements I need. Maybe a one-time flat file initialization during runtime is a way to go.
Daniel Bleisteiner
That should work ... if i may be so bold, please consider updating this question to include what you eventually went with
Everyone
I've posted my solution meanwhile... will checkmark it tomorrow.
Daniel Bleisteiner
A: 

My original idea was to map the Enum using the @Enumerated annotion. This would have looked like the following example:

@Enumerated(STRING)
private DescriptorEnum descriptor;

The database would have a column called DESCRIPTOR of type varchar and Hibernate (in my case) would map the string to the enumeration.

But I have that limit of 65k (see question) which is to small in my case. But I've found a solution. Have a look at the following example:

public final class Descriptor {
    public final String acronym;
    private static final Hashtable<String, Descriptor> all = new Hashtable<String, Descriptor>();
    static {
        initialize();
    }
    private static void initialize() {
        new Descriptor("example001");
        new Descriptor("example002");
        new Descriptor("example003");
    }
    private Descriptor(String acronym) {
        this.acronym = acronym;
        if (all.contains(this.acronym)) {
            throw new RuntimeException("duplicate acronym: " + this.acronym);
        }
        all.put(this.acronym, this);
    }
    public static Descriptor valueOf(String acronym) {
        return all.get(acronym);
    }
    public String value() {
        return this.acronym;
    }
}

This Descriptor class simulates the usage of a typical enum. But now I'm able to split the initialize() method into several ones that work around the 65k limit that also exists for methods. The Enum doesn't allow to split the initialization into several chunks - my class does.

Now I have to use a slightly different mapping though:

@Column(name = "DESCRIPTOR")
private String                  descriptorAcronym       = null;
private transient Descriptor    descriptor              = null;
public Descriptor getDescriptor() {
    return descriptor;
}
public void setDescriptor(Descriptor desc) {
    this.descriptor = desc;
    this.descriptorAcronym = desc != null ? desc.acronym : null;
}
public String getDescriptorAcronym() {
    return descriptorAcronym;
}
public void setDescriptorAcronym(String desc) {
    this.descriptorAcronym = desc;
    this.descriptor = desc != null ? Descriptor.valueOf(desc) : null;
}
@PostLoad
private void syncDescriptor() {
    this.descriptor = this.descriptorAcronym != null ? Descriptor.valueOf(this.descriptorAcronym) : null;
}

This way I can use the class like an Enum in most cases. It's a bit tricky... but it seems to work. Thanks for all the input that finally led me to that solution.

Daniel Bleisteiner