Ключевым моментом является следующее:
==
между двумя ссылочными типами всегда является ссылочным сравнением. Часто чаще всего, например, с Integer
и String
, вы хотите использовать equals
вместо ==
между ссылочным типом, а числовой примитивный тип всегда является числовым сравнением. Тип ссылки будет подвергнут распаковке преобразование Unboxing null
всегда выбрасывает NullPointerException
String
, на самом деле это НЕ примитивный тип Вышеприведенные утверждения для любого заданного действительного кода Java. С этим пониманием в представленном фрагменте нет никакой несогласованности.
Вот соответствующие секции JLS:
[д0] JLS 15.21.3 ссылочных операторов равенства
==
и!=
Если операнды операции равенства оба либо ссылочного типа или нуль , тогда операция является равенством объекта.
blockquote>Это объясняет следующее:
Integer i = null; String str = null; if (i == null) { // Nothing happens } if (str == null) { // Nothing happens } if (str == "0") { // Nothing happens }
Оба операнда являются ссылочными типами, и именно поэтому
==
является сравнительным сравнением равенства.Это также объясняет следующее:
System.out.println(new Integer(0) == new Integer(0)); // "false" System.out.println("X" == "x".toUpperCase()); // "false"
Для
==
для численного равенства по крайней мере один из операнда должен быть числовой тип :JLS 15.21.1 Операторы числового равенства
==
и!=
Если операнды оператор равенства являются как числовыми, так и одно числовыми, а другой - с числовым типом, двоичное числовое продвижение n выполняется на операндах. Если продвинутым типом операндов является
int
илиlong
, то выполняется целочисленный тест равенства; если продвинутый тип равенfloat or
double`, тогда выполняется тест равенства с плавающей запятой.Обратите внимание, что бинарное числовое продвижение выполняет преобразование набора значений и преобразование распаковки.
blockquote>Это объясняет:
Integer i = null; if (i == 0) { //NullPointerException }
Вот выдержка из Эффективное Java 2nd Edition, пункт 49: Предпочитают примитивы для примитивов в штучной упаковке :
В общем, используйте примитивы, предпочитая вложенные в бокс примитивы, когда у вас есть выбор. Примитивные типы проще и быстрее. Если вы должны использовать примитивы в штучной упаковке, будьте осторожны! Автобоксинг уменьшает многословие, но не опасность использования примитивов в штучной упаковке. Когда ваша программа сравнивает два вложенных в бокс примитива с оператором
blockquote>==
, это делает сравнение идентичности, которое почти наверняка не то, что вы хотите. Когда ваша программа выполняет смешанные вычисления с использованием примитивов с боксами и unboxed, она делает unboxing, и когда ваша программа делает unboxing, она может броситьNullPointerException
. Наконец, когда ваши программные поля вводят примитивные значения, это может привести к дорогостоящим и ненужным созданиям объектов.Есть места, где у вас нет выбора, кроме как использовать примитивы в штучной упаковке, например. generics, но в противном случае вам следует серьезно подумать, оправдано ли решение использовать бокс-примитивы.
Ссылки
- JLS 4.2. Примитивные типы и значения «Числовые типы - это интегральные типы и типы с плавающей запятой».
- JLS 5.1.8 Преобразование распаковки «Тип называется конвертируемым в числовой тип , если он является числовым типом, или он является ссылочный тип, который может быть преобразован в числовой тип путем распаковки преобразования ». «Unboxing conversion преобразует [...] из типа
Integer
в типint
» «Еслиr
-null
, то при распаковке преобразования вызываетсяNullPointerException
"- язык Java Guide / Autoboxing
- JLS 15.21.1 Операторы числового равенства
==
и!=
- JLS 15.21.3 Ссылка Операторы равенства
==
и!=
- JLS 5.6.2 Двоичное числовое продвижение
Связанные вопросы
- При сравнении двух
Integers
в Java происходит автоматическое разблокирование?- Почему эти
==
, но неequals()
?- Java: В чем разница между автобоксингом и литьем?
Связанные вопросы
- В чем разница между int и Integer в Java / C #?
- Гарантировано ли, что новый Integer (i) == i в Java? (ДА !!! Ящик распакован, а не наоборот!) [/ g49]
- Почему
int num = Integer.getInteger("123")
выбрасываетNullPointerException
? (!!!) [/ g50 ]- Java noob: generics over object s / g14] (да, к сожалению)
- Java
String.equals
по сравнению с==
В.NET Core и.NET> 4 существует универсальный метод синтаксического анализа :
Enum.TryParse("Active", out StatusEnum myStatus);
Это также включает C#7, новый встроенный out
переменные, таким образом, это делает синтаксический анализ попытки, преобразование в явный перечислимый тип и initialises+populates myStatus
переменная.
, Если у Вас есть доступ к C#7 и последней.NET, это - лучший способ.
В.NET это довольно ужасно (до 4 или выше):
StatusEnum MyStatus = (StatusEnum) Enum.Parse(typeof(StatusEnum), "Active", true);
я склонен упрощать это с:
public static T ParseEnum<T>(string value)
{
return (T) Enum.Parse(typeof(T), value, true);
}
Тогда я могу сделать:
StatusEnum MyStatus = EnumUtil.ParseEnum<StatusEnum>("Active");
Одна опция, предложенная в комментариях, состоит в том, чтобы добавить расширение, которое достаточно просто:
public static T ToEnum<T>(this string value)
{
return (T) Enum.Parse(typeof(T), value, true);
}
StatusEnum MyStatus = "Active".ToEnum<StatusEnum>();
Наконец, можно хотеть иметь перечисление по умолчанию, чтобы использовать, если строка не может быть проанализирована:
public static T ToEnum<T>(this string value, T defaultValue)
{
if (string.IsNullOrEmpty(value))
{
return defaultValue;
}
T result;
return Enum.TryParse<T>(value, true, out result) ? result : defaultValue;
}
, Который делает это вызовом:
StatusEnum MyStatus = "Active".ToEnum(StatusEnum.None);
Однако я был бы тщательным добавлением дополнительного метода как это к string
как (без управления пространством имен), появится на всех экземплярах [1 110], содержат ли они перечисление или не (таким образом 1234.ToString().ToEnum(StatusEnum.None)
было бы допустимо, но бессмыслен). Это часто быть лучшим постараться не создавать помехи базовым классам Microsoft дополнительными методами, которые только применяются в очень определенных контекстах, если у Вашей всей группы разработчиков нет очень хорошего понимания того, что делают те расширения.
Перечисление. Синтаксический анализ является Вашим другом:
StatusEnum MyStatus = (StatusEnum)Enum.Parse(typeof(StatusEnum), "Active");
object Enum.Parse(System.Type enumType, string value, bool ignoreCase);
Поэтому, если бы у Вас было перечисление, названное настроением, оно было бы похоже на это:
enum Mood
{
Angry,
Happy,
Sad
}
// ...
Mood m = (Mood) Enum.Parse(typeof(Mood), "Happy", true);
Console.WriteLine("My mood is: {0}", m.ToString());
Вы ищете Перечисление. Синтаксический анализ .
SomeEnum enum = (SomeEnum)Enum.Parse(typeof(SomeEnum), "EnumValue");
// str.ToEnum<EnumType>()
T static ToEnum<T>(this string str)
{
return (T) Enum.Parse(typeof(T), str);
}
Обратите внимание, что производительность Enum.Parse()
ужасна, потому что она реализована через отражение. (То же верно Enum.ToString
, который идет другим путем.)
, Если необходимо преобразовать строки в Перечисления в чувствительном к производительности коде, лучший выбор состоит в том, чтобы создать Dictionary<String,YourEnum>
при запуске и использовании что сделать преобразования.