Дарт инт и двойной интернированный? Лечится специально одинаковым ()?
Дарт имеет оба:
- оператор равенства
==
а также - функция верхнего уровня с именем
identical()
,
По выбору синтаксиса вполне естественно хотеть использовать дартс ==
оператор чаще, чем identical()
и мне это нравится. На самом деле, в разделе "Равенство идиоматических дротиков" говорится, что "на практике вам понадобится редко" identical()
,
В недавнем ответе на один из моих вопросов, касающихся пользовательских фильтров, похоже, что Angular Dart одобряет использование identical()
скорее, чем ==
при попытке определить, достигли ли изменения в модели устойчивого состояния. (Что может иметь смысл, я полагаю, для больших моделей по соображениям эффективности.)
Это заставило меня задуматься о личности int
и так я написал несколько тестов identical()
над int
s. Хотя я ожидал, что маленький int
s может быть "интернирован / кэширован" (например, аналогично тому, что делается в Java Integer.valueOf()
), к моему удивлению, я не могу генерировать два int
которые равны, но не идентичны. Я получаю похожие результаты для double
,
Являются int
а также double
значения интернируются / кэшируются? Или, может быть identical()
специально к ним относится? Исходя из фона Java, я привык приравнивать уравнение Дарт:
==
к Явеequal()
метод иidentical()
к тесту равенства Java==
,
Но это сейчас кажется неправильным. Кто-нибудь знает, что происходит?
3 ответа
Похоже, я отправил слишком быстро. Я только что наткнулся на Dart Issue 13084: Spec говорит, что идентичные (1.0, 1) верны, даже если они имеют разные типы, что привело меня к разделу Dart об объектной идентичности спецификации языка. (Ранее я искал равенство в спецификации, но не идентичность объекта.)
Вот выдержка:
The predefined dart function identical() is defined such that identical(c1, c2) iff:
- c1 evaluates to either null or an instance of
bool and c1 == c2, OR
- c1 and c2 are instances of int and c1 == c2, OR
- c1 and c2 are constant strings and c1 == c2, OR
- c1 and c2 are instances of double and one of the following holds: ...
и есть еще пункты, касающиеся списков, карт и константных объектов. Смотрите спецификации языка для более подробной информации. Следовательно, identical()
это гораздо больше, чем простой тест на равенство ссылок.
Числа обрабатываются специально. Если их битовый шаблон одинаков, они должны быть идентичными (хотя это все еще обсуждается, если это включает в себя различные версии NaN).
Основными причинами являются ожидания, утечка внутренних деталей и эффективность.
Ожидания: пользователи ожидают, что цифры будут идентичны. Это противоречит здравому смыслу, что x == y (для двух целых чисел), но не идентичны (x, y).
Утечка внутренних деталей: ВМ использует SMI (SMall Integer) для представления целых чисел в определенном диапазоне (31 бит на 32-битных машинах, 63 на 64-битных машинах). Они канонизированы и всегда идентичны. Раскрытие этой внутренней детализации может привести к противоречивым результатам в зависимости от используемой платформы.
Эффективность: виртуальная машина хочет распаковать номера, где это возможно. Например, внутри метода двойники часто перемещаются в регистры. Однако отслеживание оригинальной коробки может быть громоздким и сложным.
foo(x, y) {
var result = x;
while(y-- > 0) {
result += x;
}
return result;
}
Предположим, что виртуальная машина оптимизирует эту функцию и перемещается result
в реестр (распаковка x
в процессе). Это учитывает жесткую петлю, где result
затем эффективно модифицируется. Сложный случай случается, когда y
равно 0. Цикл не будет выполняться и foo
вернется x
непосредственно. Другими словами, должно быть верно следующее:
var x = 5.0;
identical(x, foo(x, 0)); // should be true.
Если ВМ распаковала result
переменная в методе foo
это должно было бы выделить новую коробку для result
и identical
поэтому вызов будет возвращен false
,
Изменяя определение identical
все эти проблемы избегаются. Это идет с небольшой стоимостью identical
проверьте, хотя.
Я не могу вспомнить источник этого, но где-то на dartlang.org или в трекере было сказано, что num
, int
а также double
действительно получают специальное лечение. Одним из таких специальных методов является то, что вы не можете подклассы этих типов по причинам производительности, но может быть и больше. Что именно влечет за собой этот особый подход, вероятно, могут ответить только разработчики или кто-то, кто знает спецификацию наизусть, но можно сделать одно заключение:
Числовые типы являются объектами dart - у них есть методы, которые вы можете вызывать в их экземплярах. Но у них также есть качества примитивных типов данных, как вы можете сделать int i = 3;
в то время как чистый объект должен иметь new
Ключевое слово где-то. Это отличается от Java, где существуют реальные примитивные типы и реальные объекты, обертывающие их и раскрывающие методы экземпляра.
Хотя технические детали, безусловно, более сложны, если вы думаете о числах дротиков как о комбинации объектов и примитивов, ваше сравнение с Java все же имеет смысл. В Java new Integer(5).equals(new Integer(5))
оценивается как истина, и так же 5==5
,
Я знаю, что это не очень технически правильный ответ, но я надеюсь, что все-таки полезно разобраться в поведении чисел дротиков, исходящих из Java-фона.