yf0994 / guava-libraries

Automatically exported from code.google.com/p/guava-libraries
Apache License 2.0
0 stars 0 forks source link

Add method to return multiple calls to `Supplier.get()` as an Iterable #653

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
Disclaimer: I might not have fully grasped the idea behind the Supplier 
interface (and the docs I know of are quite sparse).

Motivation: I'd like to retrieve a certain number of objects return by 
`Supplier.get()`, e.g. auto-generated domain objects with somewhat random 
attributes to use in tests.

I'm missing a method with a signature like this:

  public static <T> Iterable<T> collect(Supplier<T> supplier, int quantity)

This would help in creating a bunch of objects from a single supplier while 
using the interface for abstraction.

Original issue reported on code.google.com by j...@nwsnet.de on 8 Jul 2011 at 10:19

GoogleCodeExporter commented 9 years ago
Since a Supplier usually points at a single resource, I don't see this being 
awfully useful, however you could make a shim class like so;

Disclaimer: I am just writing this in the comment box, it may be broken!

class SupplierFactory {
   public static <T> Supplier<Iterable<T>> collectingSupplier(Supplier<T> supplier, int quantity) {
      return new CollectingSupplier<T>(supplier,quantity);
   }

   static final CollectingSupplier<T> extends ForwardingObject implements Supplier<Iterable<T>> {
      private final Supplier<T> delegate;
      private final int quantity;
      CollectingSupplier(Supplier<T> delegate, int quantity) {
         super();
         this.delegate = Preconditions.checkNotNull(delegate,"Delegate supplier cannot be null!");
         this.quantity = quantity;
         Preconditions.checkArgument(quantity > 0,"Quantity must be 1 or more!");
      }

      public Iterable<T> get() {
         List<T> ret = Lists.newArrayList();
         for(int i = 0; i < quantity; i++) {
            ret.add(delegate().get());
         }
         return ret;
      }      

      protected Supplier<T> delegate() {
         return delegate;
      }

   }

}

Original comment by emily@soldal.org on 13 Jul 2011 at 11:57

GoogleCodeExporter commented 9 years ago
Looking at my code again, I chose to replace the counting `for` loop which adds 
to a list with an endless iterable and called `Iterables.limit` on it (passing 
an iterable around is so much more fun ...):

  public static <T> Iterable<T> supplyLimitedly(final Supplier<T> supplier, int quantity) {
    return Iterables.limit(supplyEndlessly(supplier), quantity);
  }

  public static <T> Iterable<T> supplyEndlessly(final Supplier<T> supplier) {
    return new Iterable<T>() {

      @Override
      public Iterator<T> iterator() {
        return new UnmodifiableIterator<T>() {

          @Override
          public boolean hasNext() {
            return true;
          }

          @Override
          public T next() {
            return supplier.get();
          }
        };
      }
    };
  }

Original comment by j...@nwsnet.de on 13 Jul 2011 at 4:01

GoogleCodeExporter commented 9 years ago
We don't have a strong opinion either way on how useful this would be, but if 
we do add something like this I think it would make most sense to return an 
Iterator.  It is an iterator that extremely easy for users to write themselves, 
though.

Original comment by kevinb@google.com on 13 Jul 2011 at 6:32

GoogleCodeExporter commented 9 years ago

Original comment by kevinb@google.com on 13 Jul 2011 at 7:00

GoogleCodeExporter commented 9 years ago

Original comment by cpov...@google.com on 13 Jul 2011 at 7:42

GoogleCodeExporter commented 9 years ago
By "extremely easy for users to write themselves" I mean this:

new Iterator<T>() {
  public boolean hasNext() { return true; }
  public void remove() { throw new UOE(); }
  public T next() { return supplier.get(); }
}

So this is only useful as a Guava method if it's going to be very widely used, 
and at this point I doubt that would be the case.

Original comment by kevinb@google.com on 18 Jul 2011 at 3:29

GoogleCodeExporter commented 9 years ago
If the "extremely easy for users to write themselves" example was wrapped into 
Iterables.limit it would look:

Iterators.limit(new Iterator<T>() {
  public boolean hasNext() { return true; }
  public void remove() { throw new UOE(); }
  public T next() {     return supplier.get(); }
}, 10);

Still, this code is not extremely simple:
- requires use of final Supplier variable in outer scope
- it adds 13 unnecessary lines when compared to 
Iterators.fromSupplier(supplier, 10)
or event to
Iterators.limit(Iterators.fromSupplier(supplier), 10)
- it puts the limit value at far end (after those 13 obfuscating lines) making 
code less readable

Last but not least, it's not the first time i return to this issue searching 
for "guava iterator supplier" :)

Original comment by piotr.fi...@gmail.com on 15 Feb 2012 at 1:04

GoogleCodeExporter commented 9 years ago
You haven't answered the question of "what actual real-life use cases are there 
for this."

Original comment by wasserman.louis on 15 Feb 2012 at 3:35

GoogleCodeExporter commented 9 years ago
Again, there would have to be a *lot* of need for this to add it to Guava.  And 
note that in many cases the user could have just implemented Iterator instead 
of Supplier in the first place.  Not *your* case, I'm sure, but still.

Random note: the longhand is actually one line shorter if you extend 
UnmodifiableIterator instead of Iterator.

Original comment by kevinb@google.com on 15 Feb 2012 at 5:17

GoogleCodeExporter commented 9 years ago
… and it would be another line shorter (actually four, cause you'd want to 
format your code properly) if there'd be an `InfiniteIterator`.

I wrote such a class, and it would make sense for it to be unmodifiable (i.e. 
extend `UnmodifiableIterator`), too. I mostly use it to generate random 
(business) objects in tests, and it works great with `Iterables.limit`.

Original comment by j...@nwsnet.de on 15 Feb 2012 at 7:05

GoogleCodeExporter commented 9 years ago
As to "a lot of need" — well, being a single person i cannot create "lot of 
need". can I?

As to "what actual real-life use cases are there for this" — i  think mostly 
in test code that generates something. — And if it indeed pertains testing, 
does it mean it is no fit for Guava?

Original comment by piotr.fi...@gmail.com on 16 Feb 2012 at 12:56

GoogleCodeExporter commented 9 years ago
You can suggest a variety of use cases other than your own, point to code you 
found on Google Code with a similar need, or point to SO questions asking for 
similar things.

Original comment by wasserman.louis on 16 Feb 2012 at 3:14

GoogleCodeExporter commented 9 years ago
Or just let this rest and when we start to hear the same request from multiple 
angles then we'll know more.

Original comment by kevinb@google.com on 16 Feb 2012 at 4:05

GoogleCodeExporter commented 9 years ago
The method would be useful if one needs "generalized collection fill". For 
example I need to initialize List<Set<Foo>> such that every element of list is 
new independent empty set (e.g. not Suppliers.ofInstance(Sets.newHashSet()) 
!!!).

The requested method can be implemented as one-liner:

public static <T> Iterable<T> collect(Supplier<T> supplier, int quantity) {
    return Iterables.transform(Collections.<Supplier<T>>nCopies(quantity,supplier),Suppliers.<T>supplierFunction());
}

so I don't require Guava team to add it to Guava, however I would appreciate it 
too.

At least removing @Beta annotation from the supplierFunction method would make 
me feel safe :-).

Original comment by tomas.za...@gmail.com on 17 Feb 2012 at 4:24

GoogleCodeExporter commented 9 years ago
This issue has been migrated to GitHub.

It can be found at https://github.com/google/guava/issues/<id>

Original comment by cgdecker@google.com on 1 Nov 2014 at 4:15

GoogleCodeExporter commented 9 years ago

Original comment by cgdecker@google.com on 3 Nov 2014 at 9:09