3

在 Java 中Reader,用于拉取对象(而不是字符)的 a 的抽象版本是Iterator.

问题是有一个抽象版本Appendable或者Writer我可以在哪里推送对象(即接口)?

过去我只是制作自己的界面,例如:

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 出来,我只会使用闭包,但闭包仍然需要一个接口

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

由于它的AbstractIterator,Guava 确实提供了与 Python 的生成器类似的东西。

我向 Guava 添加了一个增强问题,但我同意他们的观点,即添加这样的基本内容并不是他们的工作。

4

4 回答 4

3

在现在的几个项目中,我为此目的定义了我称之为sink的东西:

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

这样,产生 type 对象的方法T将需要 type 的参数Sink<? super T>

出现了几个设计问题:

  • 如声明的那样,Sink#put()不引发检查异常。这不适用于通常抛出的 I/O 操作IOException。为了解决这个问题,您可以添加一个类型参数来扩展Exception并宣传put()抛出此类型,但此时,如果您对价值消费的本质了解很多,您最好为它定义一个自定义接口。

  • 如声明的那样,Sink#put()不返回值。无法向调用者指示该值是否被接受。

  • 使用像这样的通用接口,您被迫将原始类型的贡献装箱,例如intand char,这也意味着它们可以为空。考虑用 注释contribution参数@NonNull

为了配合这种类型,与 Petr Pudlák 在他的回答中提到的生成器概念相关,我定义了一个接口:

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 的原始 Puttable接口和Takable未将其放入包中的接口,尽管缺少与定时等待和方法java.util.concurrent等效的接口。Puttable#offer()Takable#poll()

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

除了我自己的库之外,我还看到 Guava 库为与散列相关的目的PrimitiveSink提供和Funnel类型。您可能会发现这些也是有用的抽象。

于 2012-08-30T17:06:30.667 回答
1

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

  1. 迭代器的对偶是生成器。迭代器“使用”集合中的值,生成器“提供”它们。但是生成器与编写器有点不同。对于作家来说,您决定何时将元素推入其中。另一方面,生成器为您提供一系列值,一个接一个。Java 对生成器没有任何特定的语言支持。另请参阅迭代器和生成器之间有什么区别?

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

于 2012-08-30T14:24:47.493 回答
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 出现时,我所要做的就是进行名称空间切换。

于 2012-08-31T19:58:36.273 回答
0

最接近的是Observable但它并没有被太多使用。

public update(Observable o, Object arg)

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

一个常见的模式是不使用接口而是使用注释。

例如

@Subscriber
public void onUpdate(Update update) { }

@Subscriber
public void onInsert(Insert insert) { }

@Subscriber
public void onDelete(Delete delete) { }

当这个类被添加为一个监听器时,它订阅Update,InsertDelete对象,并忽略任何其他的。这允许一个对象以类型安全的方式订阅不同类型的消息。

于 2012-08-30T14:04:05.080 回答