通用编写器/输出器。读者对Iterator来说是什么,X的作者是什么?

时间:2012-08-30 13:55:33

标签: java iterator guava

在Java中,使用对象(而不是字符)的Reader的抽象版本为Iterator

问题是有AppendableWriter的抽象版本,我可以推送对象(即界面)吗?

过去我只是创建自己的界面:

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

在大多数环境中是否有可用的通用接口,这是有道理的,所以我不必继续创建上述接口?

更新

这是一个有用的例子:

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);
                }
            }
        }
    });
}

我在这里使用了Appendable,但我更愿意进行Pusher回调。相信我一次Java 8 comes out I would just use closure but that closure still needs an interface

最后我之前选择的另一个选项是完全违反Guava的PredicateFunction(尽管这看起来更糟)。它违反了合同,因为这些都是幂等的(虽然我想如果你一直返回true ......)。

Guava确实提供的内容与Python的AbstractIterator类似,与Python的生成器类似。

我添加了一个enhancement issue to Guava,但我同意他们认为添加一些基本的东西并不是他们真正的工作。

4 个答案:

答案 0 :(得分:3)

现在在几个项目中,我已经为此目的定义了我称之为接收器的

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

这样,生成T类型对象的方法将需要类型为Sink<? super T>的参数。

出现了几个设计问题:

  • 如声明的那样,Sink#put()不会抛出任何已检查的异常。这对于通常抛出IOException的I / O操作不能很好地发挥作用。为了解决这个问题,你可以添加一个扩展Exception的类型参数,并宣传put()抛出这种类型,但是在那时,如果你对价值消费的本质有很多了解,那你很可能最好为它定义一个自定义界面。

  • 如声明的那样,Sink#put()不会返回值。无法向呼叫者表明该值是否被接受。

  • 使用这样的通用接口,您不得不包含intchar等基本类型的贡献,这也意味着它们可以为空。请考虑使用contribution注释@NonNull参数。

为了与这个类型相关,与Pudlák在其答案中提到的 generator 概念相关,我已经定义了 source 界面:

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

希望从这样的源中绘制类型T的项目的方法需要类型为Source<? extends T>的参数。

为了协调并发流程中的渠道,我已将Sink#put()Source#get()定义为抛出InterruptedException

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


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

这些类似于Doug Lea's original PuttableTakable接口没有进入java.util.concurrent包,但缺少与等待时间等效的PrimitiveSink 1}}和Puttable#offer()方法。

然后出现可以轻松组合的各种实现,例如交换器,过滤器和变换器。

在我自己的图书馆之外,我看到Guava图书馆为Funnel提供hashing-related purposes和{{3}}类型。您可能会发现这些也是有用的抽象。

答案 1 :(得分:1)

关于这个问题可以有几种观点:

  1. 迭代器的对偶是generator。迭代器“消耗”集合中的值,生成器“提供”它们。但是发电机与作者有点不同。对于作家,您决定何时将元素推入其中。另一方面,生成器为您提供一系列值,一个接一个。 Java对生成器没有任何特定的语言支持。另请参阅What is the difference between an Iterator and a Generator?

  2. 与迭代器相反的是你可以将值推入的东西。我不认为Java有任何抽象。我看到的关闭是Scala的Growable(忽略了clear()方法)。

答案 2 :(得分:0)

最接近的是Observable但是没有那么多使用。

public update(Observable o, Object arg)

我不会使用Iterable而不是Reader,我会创建一个您选择的消费者。

常见的模式是不使用界面而是使用注释。

e.g。

@Subscriber
public void onUpdate(Update update) { }

@Subscriber
public void onInsert(Insert insert) { }

@Subscriber
public void onDelete(Delete delete) { }

将此类添加为侦听器时,它会订阅UpdateInsertDelete个对象,并忽略其他任何对象。这允许一个对象以Type安全方式订阅不同类型的消息。

答案 3 :(得分:0)

这是我决定做的事情(而且我认为这是其他人给出的最佳选择:P)。

我将向后移植Java 8的Lambda类java.util.functions.*)。特别是这一个:

/**
 * 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.
}

基本上我会创建一个像com.snaphop.backport.java.util.functions.*这样的新命名空间,然后移动接口并使它们与Guava一起工作。显然我不会有lambda语法或扩展方法,但我可以解决这些问题。理论上,当Java 8出现时,我所要做的就是命名空间切换。