Перечисление, превышающее 65 535-байтовый предел статического инициализатора …, что, является лучшим, чтобы сделать?

Я запустил довольно большое Перечисление так называемых Дескрипторов, которые я хотел использовать в качестве списка ссылок в своей модели. Но теперь я столкнулся с пределом compiler/VM в первый раз и таким образом, я ищу лучшее решение обработать это.

Вот моя ошибка: код для статического инициализатора превышает 65 535-байтовый предел

Ясно, куда это прибывает из - мое Перечисление просто имеет далеко к очень элементам. Но мне нужны те элементы - нет никакого способа уменьшить тот набор.

Initialy, который я имею наструганный для использования единственного Перечисления, потому что я хочу удостовериться, что все элементы в Перечислении уникальны. Это используется в Быть в спящем режиме контексте персистентности, где ссылка на Перечисление хранится как Строковое значение в базе данных. Таким образом, это должно быть уникально!

Содержание моего Перечисления может быть разделено на несколько групп элементов, принадлежащих вместе. Но разделение Перечисления удалило бы уникальную безопасность, которую я получаю в течение времени компиляции. Или это может быть достигнуто с несколькими Перечислениями в некотором роде?

Моя единственная текущая идея состоит в том, чтобы определить некоторого Interface под названием Дескриптор и кодировать несколько Перечислений, реализовав его. Таким образом, я надеюсь смочь использовать Быть в спящем режиме отображение Перечисления, как будто это было единственное Перечисление. Но я даже не уверен, будет ли это работать. И я освобождаю уникальную безопасность.

Какие-либо идеи, как обработать тот случай?

19
задан Daniel Bleisteiner 30 March 2010 в 15:28
поделиться

5 ответов

Моя первоначальная идея заключалась в том, чтобы отобразить Enum с помощью аннотации @Enumerated. Это выглядело бы, как в следующем примере:

@Enumerated(STRING)
private DescriptorEnum descriptor;

В базе данных будет столбец с именем DESCRIPTOR типа varchar, а Hibernate (в моем случае) отобразит строку в перечисление.

Но у меня есть предел в 65k (см. Вопрос), который в моем случае слишком мал. Но я нашел решение. Взгляните на следующий пример:

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;
    }
}

Этот класс дескриптора имитирует использование типичного перечисления. Но теперь я могу разделить метод initialize () на несколько, которые работают с пределом в 65 КБ, который также существует для методов. Enum не позволяет разбить инициализацию на несколько частей - мой класс это делает.

Теперь мне нужно использовать немного другое отображение:

@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;
}

Таким образом, я могу использовать класс как Enum в большинстве случаев. Это немного сложно ... но вроде работает. Спасибо за весь вклад, который, наконец, привел меня к этому решению.

0
ответ дан 30 November 2019 в 05:04
поделиться

Это непростое решение, но вы можете попробовать ... исправить компилятор Java.

Когда вы пишете перечисление , компилятор Java генерирует класс, который расширяет java.lang.Enum (возможно, несколько классов, если есть методы, зависящие от константы). Класс имеет несколько (скрытых) статических полей, которые на уровне байт-кода инициализируются специальным методом () (который JVM вызывает при первом использовании класса). Как и любой другой метод, код для метода () ограничен 65535 байтами. Каждая константа составляет от 20 до 22 байтов в байт-коде () (больше, если есть конструкторы, зависящие от константы), поэтому вы достигаете предела примерно в 3000 констант перечисления.

Теперь метод () имеет забавное название, но в нем нет ничего особенного; он может вызывать другие методы. Компилятор Java мог разделить гигантского () на несколько скрытых подметодов, которые затем () вызывали один за другим. Компилятор Java в настоящее время этого не делает, но теоретически может. Результат будет обработан любой JRE.

В качестве альтернативы, синтезируйте свой класс enum синтетически, генерируя байт-код из специальной программы, возможно, самой написанной на Java. По сути, это похоже на написание собственного специализированного компилятора для конкретной цели и с использованием собственного синтаксиса исходного кода. Библиотека BCEL может помочь.

Обратите внимание, что есть и другие ограничения, которые могут вас подскочить.Для каждой константы перечисления статический код (тот, что в () ) использует две «константы», которые представляют собой внутренние значения, агрегированные в части «пула констант» скомпилированного класса. Два значения - это имя константы (в виде строки) и результирующая ссылка на статическое поле. Существует жесткое ограничение на 65536 записей пула констант (индексы 16-битные), поэтому не более 32000 констант перечисления. Исправленный компилятор Java может обойти этот предел, создав несколько скрытых классов, каждый со своим собственным постоянным пулом. Более жесткое ограничение находится в количестве статических полей: каждая константа перечисления становится статическим полем в классе "enum", и в классе может быть не более 65535 полей (статических или нет).

5
ответ дан 30 November 2019 в 05:04
поделиться

Простота. Не используйте для этого enum. Вы не можете. Это не сработает.

Есть вероятность, что в вашем исходном коде нет явных ссылок на многие значения перечисления. Скорее, вы используете перечисление как удобный способ сопоставления между уникальными экземплярами объектов и строковыми именами. Поэтому просто замените тип enum на тип, который явно управляет сопоставлением, инициализируя его путем чтения из файла или базы данных. Если вы все сделаете правильно, вы получите вычислительные свойства и безопасность типов перечисления. Единственное, что вы потеряете, это синтаксический сахар ... и статику.

У этого подхода есть дополнительное преимущество: вы можете изменять отображение "дескрипторов" без изменения исходного кода вашей программы.


Кстати, ограничение, с которым вы столкнулись, накладывается форматом файла класса JVM. Метод или конструктор имеет верхний предел размера 2^16 байт, а статический код инициализации класса представлен в виде специального метода с забавным именем.

UPDATE

К сожалению, ваше решение с самостоятельным ответом все равно столкнется с другим ограничением в 64К... если зайти слишком далеко. Разделение метода initialize() позволяет обойти ограничение на размер метода, но существует также ограничение в 64К на количество записей в пуле констант класса. Каждый литерал String требует записи в пуле констант.

8
ответ дан 30 November 2019 в 05:04
поделиться

Вы можете попробовать шаблон typeafe enum , описанный Джошуа Блохом в первом издании его книги «Эффективная Java».

Идея состоит в том, чтобы создать класс с частным конструктором. Внутри этого класса вы определяете статические экземпляры любого числа, которое хотите - точно так же, как в перечислении Java.

Однако я не знаю, есть ли ограничение на количество статических членов в языке Java.

3
ответ дан 30 November 2019 в 05:04
поделиться

Вы можете попробовать вложить статические внутренние классы внутри класса верхнего уровня

0
ответ дан 30 November 2019 в 05:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: