Подтвердить что ты не робот

Являются ли имена enum интернированными в Java?

Являются ли имена переименований интернированными в Java?

т.е. гарантировано ли enum1.name() == enum2.name() в случае с тем же именем? И безопасно ли сравнивать enum.name() с строкой, которая, как гарантируется, будет интернирована.

4b9b3361

Ответ 1

Хотя нет явной гарантии этого, конечный результат должен быть таким, чтобы сравнение всегда удавалось для констант enum с идентичными именами:

enum A {enum1};
enum B {enum1};
System.out.println(A.enum1.name() == B.enum1.name()); // Prints "true"

Причиной этого является то, что компилятор Java строит подклассы enum таким образом, что они в конечном итоге вызывают enum единственный защищенный конструктор, передавая ему имя значения enum:

protected Enum(String name, int ordinal);

Имя встроено в сгенерированный код в виде строкового литерала. Согласно документации String,

Все литералы и строковые константные выражения интернированы.

Это означает неявную гарантию вашего выражения, когда имена констант enum идентичны. Однако я не буду полагаться на это поведение и вместо этого использую equals(...), потому что любой, кто читает мой код, будет царапать его голову, думая, что я допустил ошибку.

Ответ 2

Нет.

Ответ Dasblinkenlight - лучший ответ, который мы имеем до сих пор. Там он говорит:

Причиной этого является то, что компилятор Java строит подклассы Enum таким образом, чтобы они вызывали Enum's только защищенный конструктор, передавая ему имя Enum value

и там они становятся интернированными, потому что они являются строковыми константами.

Но в JLS, 8.9.2, объявления объявления Enum, вот это:

На практике компилятор, вероятно, будет отображать тип Enum на объявление параметров String и int в конструкторе по умолчанию a Enum. Однако эти параметры не указаны как "неявно объявлено", потому что разные компиляторы не должны согласовать форму конструктора по умолчанию. Только компилятор Тип Enum знает, как создавать константы Enum; Другие компиляторы могут просто полагаться на неявно объявленные public staticполя типа Enum(§8.9.3) независимо от того, как эти поля были инициализированы.

(акцент мой)

Итак, мы будем называть конструктор, но мы не вынуждены делать это каким-либо определенным образом, и мы можем управлять нашим собственным конструктором в компиляторе.

Поэтому вполне возможно, что я напишу правильный JLS-совместимый Java-компилятор, который не стал бы ставить имена каким-то образом, возможно, не имея имен, хранящихся в виде литералов. Да, это сделало бы это специально для злонамеренного нарушения вашего кода, но это было бы правильное поведение для каждой спецификации.


Практически сказано: "Да".

Каждая нормальная реализация будет интернировать строки. Я бы сказал, что безопасно принимать такое поведение. Однако это не гарантировалось, и поэтому, если бы я увидел это в реальном коде, я был бы очень неудовлетворен, даже если бы это было подробно описано в комментарии.

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

Перед тем, как использовать их, просмотрите их имена Enum и intern(). Таким образом, будет сразу понятно, что вы делаете. Это не работает надежно. См. Комментарии.

Ответ 3

С помощью строк вы захотите использовать метод equals of Strings. В стороне у вас уже есть перечисление, которое можно сравнить с оператором равенства. По какому сценарию это возникнет?

При этом да, метод .equals вернет true, если они будут одинаковыми.

Я не уверен в операторе равенства, и, не глядя на него, могу сказать, что он плохо программирует, чтобы использовать его, если он есть.

Ответ 4

Документация Oracle о Enum говорит (первая строка):

Тип перечисления - это специальный тип данных, который позволяет переменной представлять собой набор предопределенных констант. Переменная должна быть равна одному из значений, которые были предопределены для нее. Общие примеры включают направления компаса (значения NORTH, SOUTH, EAST и WEST) и дни недели.

Если это так, тогда да, ваш enum1.name() == enum2.name() гарантированно будет правдой, если имена будут одинаковыми.

Кроме того, в методе name() javadoc:

public final Строковое имя()   Возвращает имя этой константы перечисления, точно так же, как указано в объявлении enum. Большинство программистов должны использовать метод toString(), предпочитая это, так как метод toString может возвращать более удобное имя. Этот метод предназначен в первую очередь для использования в специализированных ситуациях, где правильность зависит от получения точного имени, которое не будет отличаться от выпуска к выпуску.        Возвращает: имя этой константы перечисления

Например, если у вас было две перечисления, Days и MyDays, где SUNDAY - это общее значение, == между значениями объекта перечисления, SUNDAY вернет true, когда вы сравниваете две строки - см. рабочий пример в http://ideone.com/U1Bmcw.

/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
    public static enum Day{SUNDAY, MONDAY, TUESDAY};
    public static enum MyDays{SUNDAY};

    public static void main (String[] args) throws java.lang.Exception
    {
        MyDays m = Ideone.MyDays.SUNDAY;
        Day d = Ideone.Day.SUNDAY;

        System.out.println(d.name() == m.name());
    }
}