Using two fields from different class hierarchy routes for map-filter lambda expression

Viewed 117

We have a method which receives an object of the class on the top of the class hierarchy. It uses a condition based on a field somewhere deeper in the hierarchy, and if that is fulfilled then it uses for a builder another field also somewhere deeper in the hierarchy but on a different route starting from the top class.

public Optional<SomeType> create(final TopClassInHierarchy topClass) {
  Optional<SomeType> someObject = Optional.empty();
  if (someCondition.evaluate(getFieldOne(topClass))) {
      someObject = Optional.of(new SomeType.Builder()
              .withFieldTwo(getFieldTwo(topClass))
              .build());
  }
  return someObject;

private FieldOne getFieldOne(TopClassInHierarchy topClass) { return topClass.getSomething()...getFieldOne();
private FieldTwo getFieldTwo(TopClassInHierarchy topClass) { return topClass.getSomethingElse()...getFieldTwo();

We would like to preferably condense this into one statement, something like this

SomeType.Builder builder = new SomeType.Builder();
Optional.of(topClass)
        .map(this::getFieldOne)
        .filter(someCondition::evaluate)
        .map(this::getFieldTwo) //???
        .ifPresent(builder::withFieldTwo);

However, once we map topClass down to fieldOne for the condition evaluation, afterwards we don't seem to be able to "step back" to topClass to map it down to fieldTwo for the builder. Is this feasible with one statement?

3 Answers

Shouldn't you be able to say something like:

.map(x -> new SimpleEntry<>(this.getFieldOne(x), this.getFieldTwo(x)))
.filter(e -> evaluateTheCondition(e.getKey()))
.map(Entry::getValue)
.ifPresent(builder::withFieldTwo);

As you don't need getFieldOne you shouldn't map to it otherwise you can not get back to map for another getFieldTwo; i suggest you to first use filter without Method Referencing and the if it evaluates to true map tto getFieldTwo:

Optional.of(entity)
                .filter(p->someCondition.evaluate(p.getSomething().getFieldOne()))
                .map(this::getFieldTwo) 
                .ifPresent(builder::withFieldTwo);

If think this should work:

public Optional<SomeType> create(final TopClassInHierarchy topClass) {
  Builder builder = new Builder();

  return Optional.of(topClass)
    .filter(tc -> someCondition.evaluate(getFieldOne(tc)))
    .map(tc -> builder.withFieldTwo(getFieldTwo(tc)).build());
}

someCondition.evaluate in filter needs fieldOne as input, but to keep topClass as the current state of the Optional we do not map to fieldOne. Instead the method getFieldOne is used. If the filter is passed we can map topClass to the result of the builder applied on fieldTwo retrieved by the method getFieldTwo.

Or with more intermediate mappings:

public Optional<SomeType> create(final TopClassInHierarchy topClass) {
    Builder builder = new Builder();

    return Optional.of(topClass)
            .filter(tc -> someCondition.evaluate(getFieldOne(tc)))
            .map(this::getFieldTwo)
            .map(builder::withFieldTwo)
            .map(Builder::build);
}
Related