Pete
Pete

Reputation: 3254

How to cast objects with different classes in a Java 8 stream?

Use Case

I'm using an 3rd party library where there are two very similar classes that don't implement an interface. The code currently loops through a list of items to find the first occurrence of an object using one of these classes and then converts it to a stream where it is processed. It would be nice if I could convert this code to use a stream and have it chained to the rest of my code.

Current Code

    for (Component3Choice component: components) {
        if (component instanceof OptionalComponent3Bean) {
            OptionalComponent3Bean section = (OptionalComponent3Bean) component;

            entryStream = section.getSection().getEntry().stream()
            break;
        }
        else if (component instanceof RequiredComponent3Bean) {
            RequiredComponent3Bean section = (RequiredComponent3Bean) component;

            entryStream = section.getSection().getEntry().stream();
            break;
        }
    }
    ... do something with the stream ...

Desired Code

components.stream()
  .filter(entry -> entry instanceof OptionalComponent3Bean 
                     || entry instanceof RequiredComponent3Bean)
  .findFirst()
  .map( {{ cast entry }} )
  .map( castedEntry.getSection().getEntry())
  ... continue on with my processing

Question

Is it possible cast the entry based on the previous filter in the stream?

Upvotes: 0

Views: 2406

Answers (2)

Giulio Franco
Giulio Franco

Reputation: 3230

No, nothing can save you from bad design, which is what it seems you're fighting against.

You can force a common interface by means of a wrapper, if you need to replicate boilerplate similar to this one in many places. Otherwise, I guess the best you can do is

static private IDontKnow getStream(Component3Choice c3c) {
  if (c3c instanceof OptionalComponent3Bean) {
    return ((OptionalComponent3Bean)c3c).getStream();
  } else if (c3c instanceof RequiredComponent3Bean) {
    return ((RequiredComponent3Bean)c3c).getStream();
  } else {
    return null;
  }
}

components.stream()
  .map(x -> getStream(x))
  .filter(x -> x!=null)
  .findFirst()
  .map(x -> x.getEntry().stream());
  ... continue on with yout processing

Upvotes: 3

Eran
Eran

Reputation: 393831

Not the prettiest code, but you could do :

components.stream()
          .filter(entry -> entry instanceof OptionalComponent3Bean 
                     || entry instanceof RequiredComponent3Bean)
          .map(entry -> {
                 if ((entry instanceof OptionalComponent3Bean)
                   return ((OptionalComponent3Bean) entry).getSection().getEntry().stream(); 
                 else
                   return ((RequiredComponent3Bean) entry).getSection().getEntry().stream();
                        })
          .findFirst();

This would return an Optional<Stream<Something>>.

Note that findFirst must be the last operation, since it's terminal.

Upvotes: 1

Related Questions