Unexpected equality behavior (java.lang.Long)
João Paulo Varandas
joaovarandas at inpaas.com
Wed Aug 17 11:48:27 UTC 2016
We don't need null checks for primitive types, but ...
* Any value inside a Map or a Collection won't be primitive. And could be
null.
* Also, if we are talking about any record coming from a database, 'null'
is there.
* Any value not present in a Map is returned as null from the "get" method
(for ScriptObjectMirror too)
* Any JSON (if we use Jackson or json.org objects for example) would be
parsed into a Map, and, thus would also have null values.
* Request parameters can be null if they are not provided (nowadays, if we
are talking about RESTful services, requests are mostly in JSON format
and... there are null values).
So I would say that nulls must be validated in most cases anyway.
*João Paulo Varandas*+55 11 99889-2321
joaovarandas at inpaas.com
2016-08-17 5:50 GMT-03:00 Hannes Wallnöfer <hannes.wallnoefer at oracle.com>:
> You’re right. I was referring to behavior of the typeof operator.
>
> Hannes
>
> > Am 17.08.2016 um 08:45 schrieb Attila Szegedi <szegedia at gmail.com>:
> >
> > It doesn’t… Null is its own type in JS. The typeof operator returns the
> string “object” for a null value as per http://es5.github.io/#x11.4.3,
> but that’s (weird JS) special-casing. Internally, Null is a type with one
> value, null.
> >
> >
> >> On 17 Aug 2016, at 07:56, Hannes Wallnöfer <
> hannes.wallnoefer at oracle.com> wrote:
> >>
> >> It’s unfortunate JS considers null to be of type ‚object‘. I think that
> in most practical circumstances and definitely when a number comes from a
> Java primitive you can omit the null check as Java primitives can’t be
> null, though.
> >>
> >> Hannes
> >>
> >>
> >>> Am 16.08.2016 um 16:16 schrieb João Paulo Varandas <
> joaovarandas at inpaas.com>:
> >>>
> >>> Hi Hannes.
> >>>
> >>> Thanks for reviewing this. Seeing it from this perspective:
> >>>
> >>> Longs are just like JS Number wrappers.
> >>>
> >>> Made me believe the situation was not that bad too. Telling developers
> to 'unwrap' numbers using " x = Number(x); " would not be a bad idea.
> >>>
> >>> With that in mind, any number comparison would have to be made
> unwrapping data first ... But, we would also need to test nulls first,
> because:
> >>>
> >>> While "null == 0" yields false, and obviusly "null === 0" yields false.
> >>> Number(null) === Number(0) would yield true.
> >>>
> >>> Thus, we would need:
> >>>
> >>> function unwrapNumber(x) {
> >>> return x == null ? null : Number(x);
> >>> }
> >>>
> >>> And also, to tell developers to call unwrapNumber() everytime they
> need to compare a Number ...
> >>>
> >>> I don't know if you agree with me, but now I see this as an
> anti-pattern.
> >>>
> >>>
> >>> ---
> >>>
> >>> Anyway... It seems that all the fuzz is because of that "bug" with
> Long numbers and double. And I'm sorry that I did not look further into
> that issue.
> >>>
> >>> Maybe we should look further into that, and try to make that any
> number below Number.MAX_SAFE_INTEGER (9007199254740991), work the way is
> expected inside JS evaluations ...
> >>>
> >>>
> >>>
> >>>
> >>>
> >>>
> >>>
> >>>
> >>>
> >>> João Paulo Varandas
> >>> +55 11 99889-2321
> >>> joaovarandas at inpaas.com
> >>>
> >>>
> >>> 2016-08-16 6:23 GMT-03:00 Hannes Wallnöfer <
> hannes.wallnoefer at oracle.com>:
> >>> After exploring various options I don’t think there’s anything we can
> do to go back and treat longs like numbers in Nashorn.
> >>>
> >>> The ECMA spec is quite clear that there are numbers and object, and
> comparison of the latter is by reference, not value. So having some kind of
> in-between states is not really an option. Making longs numbers again would
> be quite a small and harmless change within the language (without reverting
> to longs as internal data type). Precision would be lowered to doubles for
> large longs, but that wouldn’t be a problem for most users.
> >>>
> >>> However, the problems starts where longs as numbers inherit from
> Number.prototype. Of course, all methods in Number.prototype are specified
> for double values, and the toString methods collides with toString in
> java.lang.Long. Having a toString method that rounds large longs to the
> nearest double value is a no-go. Nor is extending all methods in
> Number.prototype to handle longs are viable options.
> >>>
> >>> There is one issue we need to fix to improve handling of longs and
> other instances of java.lang.Number though (thanks to Attila for first
> pointing this out): Currently, java.lang.Numbers not treated as numbers in
> the ToPrimitive operation with Number hint. This means
> less-than/greater-than operators will treat those number objects as strings
> instead of converting to double. I filed a bug for this, and will back port
> it to JDK 8u:
> >>>
> >>> https://bugs.openjdk.java.net/browse/JDK-8163945
> >>>
> >>> All things considered, there are some things that make me think the
> situation is not as bad as some seem to believe. For one, the new treatment
> of Longs (with the fix for the bug above) is consistent with how JavaScript
> Number objects/wrappers are treated. Comparing two Number wrappers for the
> same value will also evaluate to false with both == and === operators,
> while comparing them to the primitive number with == will evaluate to true.
> >>>
> >>> new Number(1) == new Number(1) // false
> >>> new Number(1) === new Number(1) // false
> >>> new Number(1) == 1 // true
> >>> new Number(1) === 1 // false
> >>>
> >>> So Longs are just like JS Number wrappers. They are object wrappers
> for numeric values. If needed they can easily converted to primitive
> numbers by calling the global Number() function without „new“ keyword, eg:
> >>>
> >>> if (typeof x === „object“)
> >>> x = Number(x);
> >>>
> >>> This is a common pattern in JavaScript even without Java longs, so I
> think it’s acceptable.
> >>>
> >>> Hannes
> >>>
> >>>
> >>>> Am 05.08.2016 um 15:45 schrieb Hannes Wallnöfer <
> hannes.wallnoefer at oracle.com>:
> >>>>
> >>>> Hi Joao Paulo,
> >>>>
> >>>> thanks for the report.
> >>>>
> >>>> We do realize that this change caused problems for a lot of people,
> and we are sorry for that, and thinking how we can improve things.
> >>>>
> >>>> I still think we were right to remove longs as internal number
> representation. But maybe we went to far with this, and should allow people
> to deal with longs as numbers, assuming they are all right with an eventual
> loss of presentation.
> >>>>
> >>>> This needs some discussion and thinking and experimentation. I’ll get
> back to you after the weekend.
> >>>>
> >>>> Hannes
> >>>>
> >>>>
> >>>>
> >>>>> Am 05.08.2016 um 04:40 schrieb João Paulo Varandas <
> joaovarandas at inpaas.com>:
> >>>>>
> >>>>> By the way, I just found out some bugs filed about Long objects:
> >>>>> https://bugs.openjdk.java.net/browse/JDK-8162771
> >>>>> https://bugs.openjdk.java.net/browse/JDK-8161665
> >>>>>
> >>>>> And a twitter discussion:
> >>>>> https://twitter.com/provegard/status/755010492112986112
> >>>>>
> >>>>> I'd like to bring this up again if we have more room for discussion
> ...
> >>>>> --
> >>>>>
> >>>>> 2 Long values returned from a database and sent to a javascript
> evaluation code can't be compared the way it should in JavaScript... If I
> have some validation code, I don't know if the source is some JavaScript
> input (number), or the source is a database query
> (object/Long/Integer/Double).
> >>>>>
> >>>>> It's really hard to decide when sometimes x==y should be compared as
> x.equals(y) and sometimes not... and sometimes, at the same point in code I
> should do a lot of conditional operations, to resolve a simple
> comparison... and that comparison does not work if the data source is a
> Java object or a JavaScript object.
> >>>>>
> >>>>> The problem with the current solution is that comparison between
> different types would never work ...
> >>>>>
> >>>>> Seeing that
> >>>>> jjs> new java.lang.Long(2147483648) == "2147483648"
> >>>>> returns true
> >>>>>
> >>>>> But
> >>>>> jjs> new java.lang.Long(2147483648) == new java.lang.Long(2147483648
> )
> >>>>> returns false
> >>>>>
> >>>>> Is something really hard to accept ...
> >>>>> And that "equals" approach will work ONLY if I compare Long to Long.
> And I see this as a regression when talking about evaluating JavaScript
> code.
> >>>>>
> >>>>>
> >>>>>
> >>>>> Sorry about the long post, but I would really appreciate if we could
> revalidate this and think about what the current situation represents from
> ecmascript's point of view.
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>> -08-04 22:43 GMT-03:00 João Paulo Varandas <joaovarandas at inpaas.com
> >:
> >>>>> Sorry Tomas.
> >>>>>
> >>>>> Either you did not read the message(which would be indeed a
> beginner's mistake), or you are misleading the discussion.
> >>>>>
> >>>>> What you said, about equality and references would be true if we
> were talking about Java code. This is jdk8-nashorn's mailing list and I'm
> talking about JavaScript evaluation inside the JVM.
> >>>>>
> >>>>>
> >>>>> Comparison operators inside the script engine follow JavaScript
> rules and not Java rules.
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>> João Paulo Varandas
> >>>>> +55 11 99889-2321
> >>>>> joaovarandas at inpaas.com
> >>>>>
> >>>>>
> >>>>> 2016-08-04 20:08 GMT-03:00 Tomáš Zíma <tomas at tzima.cz>:
> >>>>> A classical beginner's mistake. You are using Java objects and
> comparing references, not the values, so this is expected behavior. Simply
> compare the objects using equals().
> >>>>>
> >>>>>
> >>>>> On 4.8.2016 15:48, João Paulo Varandas wrote:
> >>>>> Hi guys!
> >>>>>
> >>>>> It seems that version 1.8.0_101 has a bug in equality for same java
> data
> >>>>> types (java.lang.Long).
> >>>>>
> >>>>> Check that:
> >>>>> jjs> new java.lang.Long(10) == new java.lang.Long(10)
> >>>>> false
> >>>>>
> >>>>> Oops!?
> >>>>>
> >>>>> See the gist:
> >>>>> https://gist.github.com/joaovarandas/51567bd3b576d48a4c574d60d5a60b
> a3
> >>>>>
> >>>>> The results for all types should be ...
> >>>>> == true
> >>>>> === true
> >>>>> equals true
> >>>>>
> >>>>> But for java.lang.Long and java.math.BigDecimal:
> >>>>> == false
> >>>>> === false
> >>>>> equals true
> >>>>>
> >>>>> Maybe we could expand the test to other classes too, but the issue
> ...
> >>>>> - It does happen in 1.8.0_101.
> >>>>> - It does not 1.8.0_91.
> >>>>> - It does happen with other classes (BigDecimal).
> >>>>> - It does not happen with String.
> >>>>> - It does not happen with Integer.
> >>>>>
> >>>>>
> >>>>> I understand this is not an expected behavior. For now I'm rolling
> back
> >>>>> to 1.8.0_91 in my environments.
> >>>>>
> >>>>>
> >>>>>
> >>>>> Do you me to file a bug?
> >>>>>
> >>>>> Thanks
> >>>>> J
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>>
> >>>>> "Esta mensagem, incluindo seus anexos, pode conter informacoes
> confidenciais e privilegiadas.
> >>>>> Se voce a recebeu por engano, solicitamos que a apague e avise o
> remetente imediatamente.
> >>>>> Opinioes ou informacoes aqui contidas nao refletem necessariamente a
> posicao oficial da Plusoft."
> >>>>>
> >>>>> "Antes de imprimir, pense em sua responsabilidade e compromisso com
> o MEIO AMBIENTE"
> >>>>
> >>>
> >>>
> >>>
> >>> "Esta mensagem, incluindo seus anexos, pode conter informacoes
> confidenciais e privilegiadas.
> >>> Se voce a recebeu por engano, solicitamos que a apague e avise o
> remetente imediatamente.
> >>> Opinioes ou informacoes aqui contidas nao refletem necessariamente a
> posicao oficial da Plusoft."
> >>>
> >>> "Antes de imprimir, pense em sua responsabilidade e compromisso com o
> MEIO AMBIENTE"
> >>>
> >>
> >
>
>
--
"Esta mensagem, incluindo seus anexos, pode conter informacoes
confidenciais e privilegiadas.
Se voce a recebeu por engano, solicitamos que a apague e avise o remetente
imediatamente.
Opinioes ou informacoes aqui contidas nao refletem necessariamente a
posicao oficial da Plusoft."
"Antes de imprimir, pense em sua responsabilidade e compromisso com o MEIO
AMBIENTE"
More information about the nashorn-dev
mailing list