do-while with Java8-Optional

血红的双手。 提交于 2019-11-30 08:58:11

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());

in Java8, the use of Optional is considered as cleaner code than checking for null references in client-code

No, it is the other way around: Optional can be used where it helps write 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:

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.

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:

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 post on this subject.

Holger

Just add the loop support to your 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

    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 streams is the right way to go. It’s similar in that your implementation encapsulates how to iterate over the Items.

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.

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

And new class for Custom Do While as below.

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.

Dropping another alternative here that is available since Java 9.

Stream.iterate(new Item(1), Item::hasNext, Item::next)
      .forEach(this::doSomething)

Where doSomething(Item item) is the method that does something with the item.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!