【发布时间】:2015-02-26 11:48:42
【问题描述】:
我在一些项目中经常使用 do-while-checkNextForNull-getNext 循环模式(不知道是否有正式名称)。但是在 Java8 中,使用 Optional 被认为是比在客户端代码中检查空引用更干净的代码。但是当在这种循环模式中使用 Optional 时,代码会变得有点冗长和丑陋,但是因为 Optional 有一些方便的方法,我希望一定存在比我下面提出的更简洁的方法。
例子:
给定以下课程。
class Item {
int nr;
Item(nr) {
this.nr = nr;
// an expensive operation
}
Item next() {
return ...someCondition....
? new Item(nr + 1)
: null;
}
}
其中第一项始终具有 nr==1 并且每个项决定下一项,并且您不想创建不必要的新项。
我可以在客户端代码中使用以下循环 do-while-checkNextForNull-getNext 模式:
Item item = new Item(1);
do {
// do something with the item ....
} while ((item = item.next()) != null);
使用 Java8-Optional,给定的类变为:
class Item {
....
Optional<Item> next() {
return ...someCondition....
? Optional.of(new Item(nr + 1))
: Optional.empty();
}
}
然后 do-while-checkNextForNull-getNext 循环模式变得有点丑陋和冗长:
Item item = new Item(1);
do {
// do something with the item ....
} while ((item = item.next().orElse(null)) != null);
orElse(null)) != null 部分感觉不舒服。
我一直在寻找其他类型的循环,但没有找到更好的循环。有更清洁的解决方案吗?
更新:
可以使用 for-each 循环同时避免空引用(使用空引用被认为是一种不好的做法)。该方案由 Xavier Delamotte 提出,不需要 Java8-Optional。
使用通用迭代器实现:
public class Item implements Iterable<Item>, Iterator<Item> {
int nr;
Item(int nr) {
this.nr = nr;
// an expensive operation
}
public Item next() {
return new Item(nr + 1);
}
public boolean hasNext() {
return ....someCondition.....;
}
@Override
public Iterator<Item> iterator() {
return new CustomIterator(this);
}
}
和
class CustomIterator<T extends Iterator<T>> implements Iterator<T> {
T currentItem;
boolean nextCalled;
public CustomIterator(T firstItem) {
this.currentItem = firstItem;
}
@Override
public boolean hasNext() {
return currentItem.hasNext();
}
@Override
public T next() {
if (! nextCalled) {
nextCalled = true;
return currentItem;
} else {
currentItem = currentItem.next();
return currentItem;
}
}
}
然后客户端代码变得非常简单/干净:
for (Item item : new Item(1)) {
// do something with the item ....
}
虽然这可能被视为违反了迭代器协定,因为 new Item(1) 对象包含在循环中,而通常情况下,for 循环会立即调用 next() 并因此跳过第一个对象。换句话说:对于第一个对象,next() 被违反了,因为它返回了第一个对象本身。
【问题讨论】:
-
你真的必须使用这种模式吗?你可以类实现
Iterable并给你一个Iterator吗?您只需实现hasNext()(这是您当前的布尔条件)和next,而不是仅实现next()。 -
@XavierDelamotte 同意,这听起来像是(又一次)过度使用 Java8 功能集。不过,这是个好问题,OP。
-
@XavierDelamotte
Iterator在基于 IO 的源上实现是出了名的尴尬。要确定它是否为hasNext,它实际上必须读取并缓存下一个元素。仅依赖单一方法的类似光标的习语实际上是首选。例如,Spliterator使用了这种方法,但有一个额外的变化。 -
@MarkoTopolnik 确实如此。然而 Guava 提供使用 AbstractIterator 来简化此类迭代器的实现。 code.google.com/p/guava-libraries/wiki/…
-
@XavierDelamotte 包装器仍然无济于事,因为
hasNext应该是一种没有延迟的无副作用方法。尤其要注意向客户端发送 I/O 错误信号所带来的困难。
标签: java loops while-loop java-8 optional