Hi Stuart!
On 3/13/19 5:45 PM, Stuart Marks wrote:
On 3/12/19 4:32 PM, Ivan Gerasimov wrote:
If there were two new subtypes of Iterable introduced: IterableOnce
and IterableMultipleTimes, then all existing Iterables could be
retrofitted to implement one of these.
It wouldn't *automatically* solve the problem of 3rd party API, which
accepts an Iterable and iterates it multiple times, but would provide
a means to change that API in a straightforward way.
Also, the static analysis tools could issue a suggestion for code
that iterates Iterable more than once to switch to
IterableMultipleTimes, so that it won't be possible to pass in an
IterableOnce.
It would also allow to strengthen the specification for
IterableMultipleTimes and state that all classes implementing it must
make it possible to obtain multiple iterators.
Hi Ivan,
This would be a cleaner type hierarchy, in the sense that it would
avoid the subtype-or-supertype issue that's being discussed in a
parallel segment of this thread. But practically speaking I don't
think it adds much value. Most Iterables can be iterated multiple
times; it seems to be the exceptional case that an Iterable is
once-only. (In the JDK, the examples of Scanner and Stream show that
things with once-only behavior avoided implementing Iterable at all.)
Yes, I agree that IterableOnce will likely be rare, comparing to
IterableMany.
While we could migrate the JDK classes to IterableMany (or whatever it
would be called), I think it's unrealistic to expect that all the
Iterable implementations out there in the wild would migrate. We'd
thus be left with Iterable and IterableMany meaning more-or-less the
same thing in perpetuity.
I'm thinking about a use case with a method that accepts Iterable and
*needs* to traverse it multiple times.
(You actually gave such example in the proposal: assertThat(Iterable)
from AssertJ).
With IterableMany it could be handled like this:
void foo(Iterable<Object> it) {
if (!(it instanceof IterableMany)) {
var list = new ArrayList<Object>();
it.forEach(list::add);
it = list;
}
for (Object x : it) { ... }
for (Object x : it) { ... }
}
On the other hand, checking if the argument is instanceof IterableOnce
wouldn't help because the base class Iterable doesn't provide guaranties
w.r.t number of traversals.
With kind regards,
Ivan
By introducing IterableOnce, we can attract retrofits by once-off
things (like Scanner and Streams) and the uncommon occurrences of
once-off things like DirectoryStream that currently implement Iterable
can be migrated to IterableOnce. Everybody else can then just stay on
Iterable.
s'marks
--
With kind regards,
Ivan Gerasimov