java 使用 Java8-Optional 做时
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28741673/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
do-while with Java8-Optional
提问by Devabc
I'm frequently using the do-while-checkNextForNull-getNext looping pattern (don't know if there is an official name for it) in some of my projects. But in Java8, the use of Optional is considered as cleaner code than checking for null references in client-code. But when using Optional in this looping pattern, the code gets a bit verbose and ugly, but because Optional has some handy methodS, I would expect that there must exist a cleaner way than the one I came up with below.
我在我的一些项目中经常使用 do-while-checkNextForNull-getNext 循环模式(不知道它是否有正式名称)。但是在 Java8 中,使用 Optional 被认为是比在客户端代码中检查空引用更干净的代码。但是当在这种循环模式中使用 Optional 时,代码会变得有点冗长和丑陋,但是因为 Optional 有一些方便的方法,我希望一定存在一种比我在下面提出的方法更简洁的方法。
Example:
例子:
Given the following class.
鉴于以下课程。
class Item {
int nr;
Item(nr) {
this.nr = nr;
// an expensive operation
}
Item next() {
return ...someCondition....
? new Item(nr + 1)
: null;
}
}
In which the first item always has nr==1 and each item determines the next item, and you don't want to create unnecessary new items.
其中第一个项目总是有 nr==1 并且每个项目决定下一个项目,并且您不想创建不必要的新项目。
I can use the following looping do-while-checkNextForNull-getNext pattern in client-code:
我可以在客户端代码中使用以下循环 do-while-checkNextForNull-getNext 模式:
Item item = new Item(1);
do {
// do something with the item ....
} while ((item = item.next()) != null);
With Java8-Optional, the given class becomes:
使用 Java8-Optional,给定的类变为:
class Item {
....
Optional<Item> next() {
return ...someCondition....
? Optional.of(new Item(nr + 1))
: Optional.empty();
}
}
And then the do-while-checkNextForNull-getNext looping pattern becomes a bit ugly and verbose:
然后 do-while-checkNextForNull-getNext 循环模式变得有点丑陋和冗长:
Item item = new Item(1);
do {
// do something with the item ....
} while ((item = item.next().orElse(null)) != null);
The orElse(null)) != null
part feels uncomfortable.
这orElse(null)) != null
部分感觉不舒服。
I have looked for other kind of loops, but haven't found a better one. Is there a cleaner solution?
我一直在寻找其他类型的循环,但没有找到更好的循环。有更清洁的解决方案吗?
Update:
更新:
It is possible to use a for-each loop while at the same time avoiding null-references (the use of null-references is considered as a bad practice). This solution has been proposed by Xavier Delamotte, and doesn't need Java8-Optional.
可以使用 for-each 循环同时避免空引用(使用空引用被认为是一种不好的做法)。该解决方案由 Xavier Delamotte 提出,不需要 Java8-Optional。
Implementation with a generic iterator:
使用通用迭代器实现:
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);
}
}
and
和
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;
}
}
}
Then client code becomes very simple/clean:
然后客户端代码变得非常简单/干净:
for (Item item : new Item(1)) {
// do something with the item ....
}
Although this may be seen as a violation of the Iterator contract because the new Item(1)
object is included in the loop, whereas normally, the for loop would immediately call next() and thus skipping the first object. In other words: for the first object, next() is violated because it returnS the first object itself.
尽管这可能被视为违反迭代器契约,因为new Item(1)
对象包含在循环中,而通常情况下,for 循环会立即调用 next() 并因此跳过第一个对象。换句话说:对于第一个对象,next() 被违反,因为它返回第一个对象本身。
回答by Eran
You can do something like this :
你可以这样做:
Optional<Item> item = Optional.of(new Item(1));
do {
Item value = item.get();
// do something with the value ....
} while ((item = value.next()).isPresent());
or (to avoid the extra variable) :
或(以避免额外的变量):
Optional<Item> item = Optional.of(new Item(1));
do {
// do something with item.get() ....
} while ((item = item.get().next()).isPresent());
回答by Marko Topolnik
in Java8, the use of Optional is considered as cleaner code than checking for null references in client-code
在 Java8 中,使用 Optional 被认为是比在客户端代码中检查空引用更干净的代码
No, it is the other way around: Optional can be used where it helpswrite cleaner code. Where it doesn't, just stick to the old idiom. Do not feel any pressure to use it if your existing idiom looks fine—and it does, in my opinion. As an example, this would be good usage of the Optional:
不,恰恰相反:Optional 可用于帮助编写更清晰代码的地方。如果没有,只需坚持旧习语。如果您现有的习语看起来不错,请不要感到使用它的任何压力 - 在我看来确实如此。例如,这将是 Optional 的良好用法:
item.next().map(Object::toString).ifPresent(System.out::println);
Since you need to break out of the loop on the first non-present Optional, this doesn't really help.
由于您需要在第一个不存在的 Optional 上跳出循环,因此这并没有真正的帮助。
However, I assume your true interest is more general: leveraging the features of Java 8 for your code. The abstraction you should pick is the Stream:
但是,我认为您的真正兴趣更广泛:为您的代码利用 Java 8 的功能。您应该选择的抽象是 Stream:
itemStream(() -> new Item(1)).forEach(item -> { ... all you need ... });
And, naturally, you can now go wild with stream processing:
而且,自然而然地,您现在可以进行流处理:
itemStream(() -> new Item(1)).filter(item.nr > 3).mapToInt(Item::nr).sum();
This is how you would construct the stream:
这是您构造流的方式:
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class ItemSpliterator extends Spliterators.AbstractSpliterator<Item>
{
private Supplier<Item> supplyFirst;
private Item lastItem;
public ItemSpliterator(Supplier<Item> supplyFirst) {
super(Long.MAX_VALUE, ORDERED | NONNULL);
this.supplyFirst = supplyFirst;
}
@Override public boolean tryAdvance(Consumer<? super Item> action) {
Item item;
if ((item = lastItem) != null)
item = lastItem = item.next();
else if (supplyFirst != null) {
item = lastItem = supplyFirst.get();
supplyFirst = null;
}
else return false;
if (item != null) {
action.accept(item);
return true;
}
return false;
}
public static Stream<Item> itemStream(Supplier<Item> supplyFirst) {
return StreamSupport.stream(new ItemSpliterator(supplyFirst), false);
}
}
With this you are a tiny step away from the ability to seamlessly parallelize your computation. Since your item stream is fundamentally sequential, I suggest looking into my blog poston this subject.
有了这个,您离无缝并行计算的能力还有一小步。由于您的项目流基本上是连续的,我建议查看我关于此主题的博客文章。
回答by Holger
Just add the loop support to your API:
只需将循环支持添加到您的 API:
class Item {
int nr;
Item(int nr) {
this.nr = nr;
// an expensive operation
}
public void forEach(Consumer<Item> action) {
for(Item i=this; ; i=new Item(i.nr + 1)) {
action.accept(i);
if(!someCondition) break;
}
}
public Optional<Item> next() {
return someCondition? Optional.of(new Item(nr+1)): Optional.empty();
}
}
Then you can simply iterate via lambda expression
然后你可以简单地通过 lambda 表达式进行迭代
i.forEach(item -> {whatever you want to do with the item});
or method references
或方法参考
i.forEach(System.out::println);
If you want to support more sophisticated operations than just forEach loops, supporting streamsis the right way to go. It's similar in that your implementation encapsulates how to iterate over the Item
s.
如果你想支持比 forEach 循环更复杂的操作,支持流是正确的方法。它的相似之处在于您的实现封装了如何迭代Item
s。
回答by Chamly Idunil
Since this is related to some kind of design i come up with below design.
由于这与某种设计有关,因此我提出了以下设计。
Create interface which support to provide optional next.
创建支持提供可选的接口。
public interface NextProvidble<T> {
Optional<T> next();
}
Item implement NextProvidble interface.
Item 实现 NextProvidble 接口。
public class Item implements NextProvidble<Item> {
int nr;
Item(int nr) {
this.nr = nr;
// an expensive operation
}
@Override
public Optional<Item> next() {
return /*...someCondition....*/ nr < 10 ? Optional.of(new Item(nr + 1)) : Optional.empty();
}
@Override
public String toString() {
return "NR : " + nr;
}
}
Here i use /...someCondition..../ as nr < 10
这里我使用 / ...someCondition..../ as nr < 10
And new class for Custom Do While as below.
以及自定义 Do While 的新类,如下所示。
public abstract class CustomDoWhile<T extends NextProvidble<T>> {
public void operate(T t) {
doOperation(t);
Optional<T> next = t.next();
next.ifPresent( nextT -> operate(nextT));
}
protected abstract void doOperation(T t);
}
Now what you have to done in your client code.
现在您必须在客户端代码中完成什么。
new CustomDoWhile<Item>() {
@Override
protected void doOperation(Item item) {
System.out.println(item.toString());
}
}.operate(new Item(1));
It may very clear. Please add your thoughts.
它可能很清楚。请补充您的想法。