3

In Java the abstract version of a Reader that works with pulling Objects (instead of characters) is an Iterator.

The question is there an abstract version of Appendable or Writer where I can push objects (ie an interface)?

In the past I just make my own interface like:

public interface Pusher<T> {
    public void push(T o);
}

Is there a generic interface that is available in most environments that someone knows about that makes sense so I don't have to keep creating the above interface?

Update:

Here is an example of where it would be useful:

public void findBadCategories(final Appendable a)  {
    String q = sql.getSql("product-category-bad");
    jdbcTemplate.query(q, new RowCallbackHandler() {
        @Override
        public void processRow(ResultSet rs) throws SQLException {
            String id = rs.getString("product_category_id");
            String name = rs.getString("category_name");
            if (! categoryMap.containsKey(id)) {
                try {
                    a.append(id + "\t" + name + "\n");
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    });
}

I'm using an Appendable here but I would much rather have my Pusher callback. Believe me once Java 8 comes out I would just use closure but that closure still needs an interface.

Finally the other option I have chosen before is to completely violate Guava's Predicate or Function (although that seems even worse). Its violation of the contract because these aim to be idempotent (although I suppose if you return true all the time... ).

What Guava does provide though is sort of analagous to Python's generators thanks to its AbstractIterator.

I added an enhancement issue to Guava but I agree with them that its not really their job to add something fundamental like that.

Adam Gent
  • 47,843
  • 23
  • 153
  • 203
  • http://docs.oracle.com/javase/7/docs/api/java/lang/Appendable.html is only for CharSequences. – Peter Lawrey Aug 30 '12 at 13:59
  • The closest thing that comes to mind is a [`Collection`](http://docs.oracle.com/javase/7/docs/api/java/util/Collection.html). That said, I don't think the analogy between a `Reader` and an `Iterator` that you see is all that meaningful. The reason why the IO subsystem has such low-level interfaces available probably isn't information hiding; instead, I'd say they're exposing the native low-level APIs to enable writing high-performance custom IO code (for databases or network servers) without the overhead of buffering. – millimoose Aug 30 '12 at 14:01
  • Ahh duh I know that :) I'm saying I want one for objects. – Adam Gent Aug 30 '12 at 14:02
  • (Alternately, because whoever designed the IO API was on an architectural binge. Python seems to make do with a single IO abstraction just fine.) – millimoose Aug 30 '12 at 14:02
  • @millimoose let me give you an example where its useful. Pulling from a giant resultset from a database you want the client to provide away to deal with the stream of objects coming in. – Adam Gent Aug 30 '12 at 14:03
  • @millimoose because of ducktyping? I know python has generators but I am not aware of some non IO push abstraction for python either. – Adam Gent Aug 30 '12 at 14:04
  • An interface with an `onResultSet(ResultSet)` method? – Peter Lawrey Aug 30 '12 at 14:04
  • @PeterLawrey I don't want the client to know about ResultSet. – Adam Gent Aug 30 '12 at 14:05
  • @AdamGent I was comparing IO APIs. Java has a huge hierarchy of classes that wrap each other to provide various layers of processing, Python only has "file objects" that support buffered, unbuffered, byte-wise and character-wise modes of operation. – millimoose Aug 30 '12 at 14:06
  • @AdamGent I think you're stuck to making your own interface here. Personally I'd just tie it to its purpose, and name it something like `QueryResultHandler` – millimoose Aug 30 '12 at 14:09
  • @millimoose Maybe Java 8 with its closures it will become more of an issue... yeah right :) – Adam Gent Aug 30 '12 at 14:11
  • @AdamGent Java 8's lambdas would still require a SAM type as a formal method argument. However, seeing as the design is (unsurprisingly) being lifted from C#, there might be some generic lambdas available in the JDK in the end. – millimoose Aug 30 '12 at 16:06

4 Answers4

3

On several projects now, I've defined for this purpose what I call a sink:

interface Sink<T> {
  void put(T contribution);
}

With that, methods that produce objects of type T would demand a parameter of type Sink<? super T>.

Several design questions arise:

  • As declared, Sink#put() throws no checked exceptions. That doesn't play well with I/O operations that usually throw IOException. To address this, you can add a type parameter that extends Exception and advertise that put() throws this type, but at that point, if you know that much about the nature of value consumption, you're probably better off defining a custom interface for it.

  • As declared, Sink#put() does not return a value. It's not possible to indicate to the caller whether the value was accepted or not.

  • With a generic interface like this, you're forced to box contributions of primitive types like int and char, which also means they can be null. Consider annotating the contribution parameter with @NonNull.

To go along with this type, related to the generator concept that Petr Pudlák mentions in his answer, I've defined a source interface:

interface Source<T> {
  T get();
}

Methods looking to draw items of type T from such a source demand a parameter of type Source<? extends T>.

For coordination with channels among concurrent processes, I've defined both Sink#put() and Source#get() to throw InterruptedException:

interface Sink<T> {
  void put(T contribution) throws InterruptedException;
}


interface Source<T> {
  T get() throws InterruptedException;
}

These are analogous to Doug Lea's original Puttable and Takable interfaces that didn't make it into the java.util.concurrent package, though lacking in an equivalent to the timed wait Puttable#offer() and Takable#poll() methods.

All sorts of implementations then arise that can be composed easily, such as exchangers, filters, and transformers.

Beyond my own library, I've seen the Guava library provide the PrimitiveSink and Funnel types for hashing-related purposes. You may find those to be useful abstractions as well.

seh
  • 14,999
  • 2
  • 48
  • 58
  • I like the `InterruptedException` idea. If you look at the example [interface I posted to Guava](http://code.google.com/p/guava-libraries/issues/detail?id=1129). I had proposed the idea of an `#isAccepting()` method but was only to be consistent with `Iterator`. But I like your method better. As for the `generators` which I was a big fan of when Python came out with I just subclass Guava's AbstractIterator but only because so many things work with `Iterator`. – Adam Gent Aug 30 '12 at 17:19
  • Also I would call the interfaces `Consumer` (Sink) and `Producer` (Source). – Adam Gent Aug 30 '12 at 17:22
  • I eschewed the words "consumer" and "producer" because I framed the interfaces from the perspective of their callers: a caller doesn't know whether something is being produced or exposed, per se; he only knows that he has a source from which to get things. Likewise, he doesn't know whether something is consuming his contributions; he only knows that there's a place to drop them. – seh Aug 30 '12 at 17:24
  • I forgot to mention this about the `Source` interface with respect to `Iterator`: an `Iterator` is not safe for use by more than one consuming thread, due to the *check-then-act* danger of the split `hasNext()` and `next()` methods. I deliberately framed `Source` as an abstraction of the head of a queue. Any number of threads can request items concurrently, and that request either succeeds by delivering a valid item, or "fails" by way of delivering a prearranged invalid sentinel item. For a finite sequence of items, a `Source`'s codomain must be one value larger than its underlying range. – seh Aug 30 '12 at 17:30
  • Well I suppose one could get into a semantic debate but my feeling is methods like `get()` already have some connotation and arguably canonical behavior of returning mostly the same thing on multiple calls (ie getter). I believe there is an abstract idea that these interfaces are used for stream like behavior... ie `put` and `get` are for containers. Maybe `Source` could be `Faucet` for fun :) – Adam Gent Aug 30 '12 at 17:34
  • I just saw your recent comment... in that case `Source` in your mind is more like Data flow variable (see Mozart programming language) or a more abstract `SynchronousQueue`. – Adam Gent Aug 30 '12 at 17:37
1

There can be several views on the subject:

  1. The dual of an iterator is a generator. Iterators "consume" values from a collection, generator "provide" them. But generators are a bit different than writers. For a writer, you decide when you push an element into it. On the other hand, generators provide you with a sequence of values, one by one. Java doesn't have any specific language support for generators. See also What is the difference between an Iterator and a Generator?

  2. The opposite to iterators is something you could push values into. I don't think Java has any abstraction for that. The closes I have seen is Scala's Growable (neglecting the clear() method).

Community
  • 1
  • 1
Petr
  • 62,528
  • 13
  • 153
  • 317
  • @PeterPudlak I get around Java's not having generators by using Guava's [AbstractIterator](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/AbstractIterator.html). BTW I know the difference between a writer, generator, and collections. So #1 is nice review for others its almost insulting to me. – Adam Gent Aug 30 '12 at 14:29
  • @AdamGent Sorry, I didn't mean anything like that, you didn't mention that you know generators in your question. – Petr Aug 30 '12 at 15:53
  • Its no problem. I think you saw the post before my edits. I do love Python's generators. – Adam Gent Aug 30 '12 at 17:24
0

The closest is Observable but it isn't used so much.

public update(Observable o, Object arg)

I would not use Iterable instead of Reader and I would create a consumer of your choice.

A common pattern is to not use an interface but rather an annotation.

e.g.

@Subscriber
public void onUpdate(Update update) { }

@Subscriber
public void onInsert(Insert insert) { }

@Subscriber
public void onDelete(Delete delete) { }

When this class is added as a listener it subscribes to Update, Insert and Delete objects, and ignores any others. This allows one object to subscribe to different type of message in a Type safe way.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Yes I use Guava's event system all the time for something similar but its rather for a long running system and not a throw away object like writer or reader is. – Adam Gent Aug 30 '12 at 14:06
0

Here is what I decided to do (and I think its the best option out of what others gave :P ).

I'm going to backport Java 8's Lambda classes (java.util.functions.*). Particularly this one:

/**
 * Performs operations upon an input object which may modify that object and/or
 * external state (other objects).
 *
 * <p>All block implementations are expected to:
 * <ul>
 * <li>When used for aggregate operations upon many elements blocks
 * should not assume that the {@code apply} operation will be called upon
 * elements in any specific order.</li>
 * </ul>
 *
 * @param <T> The type of input objects to {@code apply}.
 */
public interface Block<T> {
/**
 * Performs operations upon the provided object which may modify that object
 * and/or external state.
 *
 * @param t an input object
 */
void apply(T t);

// Some extension methods that I'll have to do with below.
}

Basically I'll make a new namespace like com.snaphop.backport.java.util.functions.* and move over the interfaces and make them work with Guava. Obviously I won't have the lambda syntax or the extension methods but those I can work around. Then in theory when Java 8 comes out it all I would have to do is a namespace switch.

Adam Gent
  • 47,843
  • 23
  • 153
  • 203