Являются ли имена переименований интернированными в Java?
т.е. гарантировано ли enum1.name() == enum2.name()
в случае с тем же именем? И безопасно ли сравнивать enum.name()
с строкой, которая, как гарантируется, будет интернирована.
Являются ли имена переименований интернированными в Java?
т.е. гарантировано ли enum1.name() == enum2.name()
в случае с тем же именем? И безопасно ли сравнивать enum.name()
с строкой, которая, как гарантируется, будет интернирована.
Хотя нет явной гарантии этого, конечный результат должен быть таким, чтобы сравнение всегда удавалось для констант 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(...)
, потому что любой, кто читает мой код, будет царапать его голову, думая, что я допустил ошибку.
Ответ Dasblinkenlight - лучший ответ, который мы имеем до сих пор. Там он говорит:
Причиной этого является то, что компилятор Java строит подклассы
Enum
таким образом, чтобы они вызывалиEnum's
только защищенный конструктор, передавая ему имяEnum
value
и там они становятся интернированными, потому что они являются строковыми константами.
Но в JLS, 8.9.2, объявления объявления Enum, вот это:
На практике компилятор, вероятно, будет отображать тип
Enum
на объявление параметровString
иint
в конструкторе по умолчанию aEnum
. Однако эти параметры не указаны как "неявно объявлено", потому что разные компиляторы не должны согласовать форму конструктора по умолчанию. Только компилятор ТипEnum
знает, как создавать константыEnum
; Другие компиляторы могут просто полагаться на неявно объявленныеpublic static
поля типаEnum
(§8.9.3) независимо от того, как эти поля были инициализированы.
(акцент мой)
Итак, мы будем называть конструктор, но мы не вынуждены делать это каким-либо определенным образом, и мы можем управлять нашим собственным конструктором в компиляторе.
Поэтому вполне возможно, что я напишу правильный JLS-совместимый Java-компилятор, который не стал бы ставить имена каким-то образом, возможно, не имея имен, хранящихся в виде литералов. Да, это сделало бы это специально для злонамеренного нарушения вашего кода, но это было бы правильное поведение для каждой спецификации.
Каждая нормальная реализация будет интернировать строки. Я бы сказал, что безопасно принимать такое поведение. Однако это не гарантировалось, и поэтому, если бы я увидел это в реальном коде, я был бы очень неудовлетворен, даже если бы это было подробно описано в комментарии.
Пожалуйста, не полагайтесь на такое неуказанное и специфичное для реализации поведение. Если вам действительно нужно, напишите unit test для этого. И поместите assert
в код, и много объяснений. Измерить, действительно ли ваш подход будет делать что-либо.
Перед тем, как использовать их, просмотрите их имена Это не работает надежно. См. Комментарии.Enum
и intern()
. Таким образом, будет сразу понятно, что вы делаете.
С помощью строк вы захотите использовать метод equals of Strings. В стороне у вас уже есть перечисление, которое можно сравнить с оператором равенства. По какому сценарию это возникнет?
При этом да, метод .equals вернет true, если они будут одинаковыми.
Я не уверен в операторе равенства, и, не глядя на него, могу сказать, что он плохо программирует, чтобы использовать его, если он есть.
Документация 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());
}
}