Hi Reinier,
I see you asked this here as well as on Stack Overflow. I'll repeat my answer from
Stack Overflow here and then continue with some additional discussion.
Quick recap: Collection::toArray has a requirement that the returned array be
"safe", whereas Stream::toArray has no such requirement. In my earlier Stack
Overflow answer (which you quoted) I mentioned something like "if [Stream.toArray]
were to violate its spec", and you asked, what spec?
My answer:
I probably mistakenly assumed that the Stream::toArray spec had the same "safety"
requirements as Collection::toArray. That was our intent, as I recall, but it
didn't make it into the spec.
Now to your questions in this email.
The second question, where to report this, is right here! We can discuss this and
file bugs/PRs etc. if necessary depending on the outcome of the discussion.
The first question has some subtlety to it. Should we not update the specification
for Stream::toArray to have the same "safety" requirement as Collection::toArray?
I guess a change would serve to clarify the specification. The developer of a
clean-room implementation of streams might reasonably think that, if the stream
source is an array, that this array can simply be returned from toArray(). However,
that violates the intent (which is pretty clear in my memory) that the returned
array be freshly created. Usually, adding assertions to interfaces creates a danger
of invalidating existing implementations. However, I don't think returning an
existing array, especially one that might be aliased, was ever considered to be
valid behavior. Thus, a spec change would be more like filling a gap that shouldn't
have been there in the first place, not adding a new semantic requirement.
As a practical matter, there are few third-party implementations, this requirement
is almost impossible to test, and any callers of toArray() won't change. That is, if
they're currently using the returned array, they'll continue using it; but if they
want 100% assurance the array isn't aliased, they'll continue to make a defensive
copy. So, it doesn't seem like changing the spec will have any practical impact.
Have I answered your questions?
s'marks
On 12/14/23 5:46 AM, Reinier Zwitserloot wrote:
Hi core libs team,
I think I found a rather inconsequential and esoteric bug, though the term is
somewhat less objectively defined when the problem exists solely in how complete
some method’s javadoc is.
Two questions: Is there a plausible argument that the javadoc as is, shouldn’t be
updated? If not, what’s the right place to report this javadoc ‘bug’?
The issue:
A snippet of the javadoc of Collection.toArray, from current HEAD jdk22u:
<p>The returned array will be "safe" in that no references to it are
* maintained by this collection. (In other words, this method must
* allocate a new array even if this collection is backed by an array).
* The caller is thus free to modify the returned array.
The javadoc of Stream.toArray, from current HEAD jdk22u - has no such rider
anywhere in its documentation nor on the javadoc of the Stream interface, nor on
the package javadoc. The javadoc is quite short; the complete docs on toArray():
/**
* Returns an array containing the elements of this stream.
*
* <p>This is a <a href="package-summary.html#StreamOps">terminal
* operation</a>.
*
* @return an array, whose {@linkplain Class#getComponentType runtime
component
* type} is {@code Object}, containing the elements of this stream
*/
The more usually used variant taking a function that makes the array has slightly
longer javadoc, but it similarly makes no mention whatsoever about the contract
stipulation that any implementors must not keep a reference.
A snippet from Stuart Marks on a stack overflow question about a to the asker
weird choice about stream’s toList()’s default implementation
(https://stackoverflow.com/questions/77473755/is-it-necessary-to-copy-a-list-to-be-safe/77474199?noredirect=1#comment136909551_77474199):
@Holger The extra step in the default implementation is there to force a defensive
copy if |this.toArray| were to violate its spec and keep a reference to the
returned array. Without the defensive copy, it would be possible to modify the
list returned from the default |toList| implementation.
Which leads to the obvious question: Where is that ’spec’ that Stuart is referring
to? Either the javadoc is the spec and therefore the javadoc is buggy, in that it
fails to mention this stipulation, or the spec is elsewhere, in which case surely
the javadoc should link to it or copy it.
Possibly this is jus filed away as: “Unlike with Collection, Stream instances are
disposable; after a terminal operation (and toArray is terminal) has been invoked
on it, that stream object has ceased being useful. Therefore, there is no
perceived value to caching any created array, therefore, it doesn’t need mentioning".
Except, as per Stuart’s comment, actual OpenJDK code is written partly to deal
with any violators of this invisible spec, and the discrepancy (where
Collection.toArray explicitly mentions the contract stipulation that toArray()
must make safe arrays, but Stream’s toArray() does not) suggests a fundamental
difference where none exists (in fact, literally: Apparently its a spec violation
if your Stream implementation return a non-safe array from toArray!)
--Reinier Zwitserloot