使用Java8时执行可选操作
在我的一些项目中,我经常使用DowhileCheckNextForFullGetNext循环模式(不知道是否有正式名称)。但是在Java8中,使用Optional被认为比检查客户机代码中的空引用更干净。但是当在这种循环模式中使用Optional时,代码会变得有点冗长和丑陋,但是因为Optional有一些方便的方法,我希望一定存在一种比我下面提出的更干净的方法 例如: 给定以下类使用Java8时执行可选操作,java,loops,while-loop,java-8,optional,Java,Loops,While Loop,Java 8,Optional,在我的一些项目中,我经常使用DowhileCheckNextForFullGetNext循环模式(不知道是否有正式名称)。但是在Java8中,使用Optional被认为比检查客户机代码中的空引用更干净。但是当在这种循环模式中使用Optional时,代码会变得有点冗长和丑陋,但是因为Optional有一些方便的方法,我希望一定存在一种比我下面提出的更干净的方法 例如: 给定以下类 class Item { int nr; Item(nr) { this.nr =
class Item {
int nr;
Item(nr) {
this.nr = nr;
// an expensive operation
}
Item next() {
return ...someCondition....
? new Item(nr + 1)
: null;
}
}
其中第一个项目的nr==1,每个项目决定下一个项目,并且您不希望创建不必要的新项目
在客户端代码中选中NextForFull 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();
}
}
orElse(null))!=空
零件感觉不舒服
我已经寻找了其他类型的循环,但没有找到更好的循环。有更清洁的解决方案吗
更新:
可以对每个循环使用一个for,同时避免空引用(使用空引用被认为是一种不好的做法)。此解决方案由Xavier Delamotte提出,不需要Java8可选
使用通用迭代器实现:
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);
}
}
尽管这可能被视为违反迭代器约定,因为循环中包含
新项(1)
对象,但通常情况下,for循环会立即调用next(),从而跳过第一个对象。换句话说:对于第一个对象,next()被违反,因为它返回第一个对象本身。您可以这样做:
Optional<Item> item = Optional.of(new Item(1));
do {
Item value = item.get();
// do something with the value ....
} while ((item = value.next()).isPresent());
Optional item=Optional.of(新项(1));
做{
Item value=Item.get();
//做一些有价值的事情。。。。
}while((item=value.next()).isPresent());
或者(为了避免额外的变量):
Optional item=Optional.of(新项(1));
做{
//对item.get()执行某些操作。。。。
}while((item=item.get().next()).isPresent());
在Java8中,使用Optional被认为比检查客户机代码中的空引用更干净
不,这是另一种方式:可选的可以用来帮助编写更干净的代码。如果没有的话,就坚持老习惯用语。在我看来,如果你现有的习语看起来不错,而且确实不错,那么不要觉得有压力去使用它。例如,这将是对可选选项的良好使用:
item.next().map(Object::toString).ifPresent(System.out::println);
因为您需要在第一个不存在的可选项上打破循环,所以这并没有真正的帮助
然而,我认为您真正感兴趣的是更普遍的:在代码中利用Java8的特性。您应该选择的抽象是流:
itemStream(() -> new Item(1)).forEach(item -> { ... all you need ... });
而且,很自然,您现在可以使用流处理:
itemStream(() -> new Item(1)).filter(item.nr > 3).mapToInt(Item::nr).sum();
以下是构建流的方式:
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);
}
}
导入java.util.Spliterators;
导入java.util.function.Consumer;
导入java.util.function.Supplier;
导入java.util.stream.stream;
导入java.util.stream.StreamSupport;
公共类ItemSpliterator扩展了Spliterators.AbstractSpliterator
{
私人供应商优先供应;
私人物品;
公用项目拆分器(供应商供应优先){
super(Long.MAX_值,有序|非空);
this.supplyFirst=supplyFirst;
}
@覆盖公共布尔tryAdvance(使用者只需将循环支持添加到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();
}
}
或方法引用
i.forEach(System.out::println);
如果您想支持比forEach循环更复杂的操作,这是正确的方法。类似的是,您的实现封装了如何在项上迭代。,因为这与我在下面设计中提出的某种设计有关
创建支持提供可选下一步的接口
public interface NextProvidble<T> {
Optional<T> next();
}
公共接口下一步提供{
可选的next();
}
项目实现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;
}
}
public类项实现NextProvidble{
国际天然气公司;
项目(国际编号){
这个。nr=nr;
//昂贵的手术
}
@凌驾
公共可选下一步(){
return/*…someCondition….*/nr<10?可选。of(新项(nr+1)):可选。空();
}
@凌驾
公共字符串toString(){
返回“NR:+NR;
}
}
这里我使用/…someCondition…../as nr<10
和自定义Do的新类,如下所示
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);
}
公共抽象类CustomDoWhile{
公共无效操作(T){
操作(t);
可选的next=t.next();
next.ifPresent(nextT->operate(nextT));
}
受保护的抽象无效操作(T);
}
现在,您必须在客户端代码中执行哪些操作
new CustomDoWhile<Item>() {
@Override
protected void doOperation(Item item) {
System.out.println(item.toString());
}
}.operate(new Item(1));
newcustomdowhile(){
@凌驾
受保护的无效操作(项目){
System.out.println(item.toString());
}
}.操作(新项目(1));
这可能很清楚。
请添加您的想法。在此处删除另一个自那时起可用的备选方案
其中,doSomething(Item Item)
是对该项执行某些操作的方法。您真的必须使用此模式吗?您是否可以将类实现为Iterable
,并为您提供一个迭代器
?您只需实现hasNext()
(这是您当前的布尔条件)和next
,而不是只实现next()
@XavierDelamotte agree,这闻起来像(又一个)过度使用Java8功能集。不过,这是一个很好的问题,OP.@XavierDelamotte迭代器
在基于IO的源代码上实现是出了名的笨拙。要想知道它是否有下一个
,它实际上必须读取和缓存下一个元素。类似光标的习惯用法,只依赖于单个方法,实际上是首选的。例如e、 Spliterator
使用了这种方法,还有一个额外的扭曲。@MarkoTopolnik确实如此。然而,Guava提供了使用AbstractIterator来简化i
i.forEach(System.out::println);
public interface NextProvidble<T> {
Optional<T> next();
}
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;
}
}
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);
}
new CustomDoWhile<Item>() {
@Override
protected void doOperation(Item item) {
System.out.println(item.toString());
}
}.operate(new Item(1));
Stream.iterate(new Item(1), Item::hasNext, Item::next)
.forEach(this::doSomething)