在Java中,使用拉对象(而不是字符)的Reader
的抽象版本为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 comes out I would just use closure but that closure still needs an interface。
最后我之前选择的另一个选项是完全违反Guava的Predicate
或Function
(尽管这看起来更糟)。它违反了合同,因为这些都是幂等的(虽然我想如果你一直返回true
......)。
Guava确实提供的内容与Python的AbstractIterator类似,与Python的生成器类似。
我添加了一个enhancement issue to Guava,但我同意他们认为添加一些基本的东西并不是他们真正的工作。
答案 0 :(得分:3)
现在在几个项目中,我已经为此目的定义了我称之为接收器的:
interface Sink<T> {
void put(T contribution);
}
这样,生成T
类型对象的方法将需要类型为Sink<? super T>
的参数。
出现了几个设计问题:
如声明的那样,Sink#put()
不会抛出任何已检查的异常。这对于通常抛出IOException
的I / O操作不能很好地发挥作用。为了解决这个问题,你可以添加一个扩展Exception
的类型参数,并宣传put()
抛出这种类型,但是在那时,如果你对价值消费的本质有很多了解,那你很可能最好为它定义一个自定义界面。
如声明的那样,Sink#put()
不会返回值。无法向呼叫者表明该值是否被接受。
使用这样的通用接口,您不得不包含int
和char
等基本类型的贡献,这也意味着它们可以为空。请考虑使用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 Puttable
和Takable
接口没有进入java.util.concurrent
包,但缺少与等待时间等效的PrimitiveSink
1}}和Puttable#offer()
方法。
然后出现可以轻松组合的各种实现,例如交换器,过滤器和变换器。
在我自己的图书馆之外,我看到Guava图书馆为Funnel
提供hashing-related purposes和{{3}}类型。您可能会发现这些也是有用的抽象。
答案 1 :(得分:1)
关于这个问题可以有几种观点:
迭代器的对偶是generator。迭代器“消耗”集合中的值,生成器“提供”它们。但是发电机与作者有点不同。对于作家,您决定何时将元素推入其中。另一方面,生成器为您提供一系列值,一个接一个。 Java对生成器没有任何特定的语言支持。另请参阅What is the difference between an Iterator and a Generator?
与迭代器相反的是你可以将值推入的东西。我不认为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) { }
将此类添加为侦听器时,它会订阅Update
,Insert
和Delete
个对象,并忽略其他任何对象。这允许一个对象以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出现时,我所要做的就是命名空间切换。