On 6/17/18 1:50 AM, Peter Levart wrote:
It's a little strange that the generator function is used to construct an
empty array (at least in the default method, but overrides will likely do the
same if they want to avoid pre-zeroing overhead) in order to just extract the
array's type from it. Someone could reasonably expect the provided function
to be called with the exact length of needed array. The
Collection.toArray(T[]) at least gives user an option to actually fully use
the provided array, if user provides the correct length.

This is actually what we're trying to avoid. The toArray(T[]) API has to deal
with the cases where the array isn't the right length, and it reallocs or
inserts an extra null in cases where it isn't. To avoid this, people do

    coll.toArray(new MyClass[coll.size()])

which turns out to be an anti-pattern. We could try to teach people to write

    coll.toArray(new MyClass[0])

instead, and this works, but it's quite non-obvious. ("Why do I need to create a
zero-length array first?") (#include Tagir's comment about caching zero-length
instances) Instead we want to direct people to write

    coll.toArray(MyClass[]::new)

which creates an array of the right type and requested length.

The argument about using (and re-using) a method so that a method reference
can be passed to the method without any unchecked casts is equally true for
any of the 3 alternatives shown - the method itself might need unchecked
casts, but its usage not:

static List<String>[] array(int len) static Class<List<String>>
elementType() static Class<List<String>[]> arrayType()

In principle all of these are possible. I don't see these as equal, though. It's quite common to have to create arrays of a generic type, either inline with unchecked casts, or possibly refactored into a method. I very rarely see Class objects of generic types.

But I can see that you want to align the API with Stream.toArray, while still
 providing the optimal implementation. It's just that the API doesn't fit the
 usecase. The function approach makes more sense in the Stream API where it
is explicitly explained that it may be used to construct arrays needed to
hold intermediary results of partitioned parallel execution too, but in
Collection API it is usually the case to just provide a copy of the
underlying data structure in the most optimal way (without pre-zeroing
overhead) and for that purpose, 2nd and 3rd alternatives are a better fit.

Sure, the Stream API has additional uses for holding intermediate results. That
doesn't imply that Collection.toArray(generator) doesn't meet its use case
(which I described above).

I also don't see how class type tokens are a better fit. A type token is
"natural" if you're thinking of implementing it in terms of Arrays.copyOf() --
which it is right now, but that's an implementation detail.

Suppose Stream took a different approach and used the 2nd or 3rd approach
(which is universal). Would then Collection API also get the same method?

I'm not sure where this is headed. I'm pretty sure we considered using type
tokens for Stream.toArray() and rejected them in favor of toArray(generator). If
there had been some reason to use a type token instead, maybe we would have used
them, in which case we'd consider modifying Collection.toArray() would take a type token as well. But I'm not aware of such a reason, so....

It might have been the case in the past when Java generics were introduced,
that class literals like List<String>.class  would just confuse users,
because most aspects of such type token would be erased and there were fears
that enabling them might limit options for reified generics in the future.
But long years have passed and java programmers have generally become
acquainted with Java generics and erasure to the point where it doesn't
confuse them any more, while reifying Java generics has been explored further
in Valhalla to the point where it has become obvious that erasure of
reference types is here to stay.

Java could enable use of class literals like List<String>.class without fear
that such literals would make users write buggy code or that such uses would
limit options for Java in the future. Quite the contrary, they would enable
users to write type-safer code than what they can write today. In the light
of future Java  (valhalla specialized generics), where such class literals
make even more sense, enabling generic class literals could be viewed as a
stepping stone that has some purpose in the short term too.
Again, I'm not sure where this is headed.

I certainly would not propose changing the language to allow generic class literals in order to support the present use case (creating an array from a collection).

As a longer term proposal, generic class literals might or might not be worthwhile on their own merits. It's certainly an irritant not having them; I could imagine some uses for them. But there are probably some downsides (such as unsoundness) that will need to be thought through.

s'marks

Reply via email to