3

I've encountered the following problem:

If I map an object in a stream to a specific class in Java, the java stream API does not recognize a specific object after the mapping and still assumes it is an object. What am I doing wrong, and is there a way to solve this without potential class cast exceptions?

Here is the code example:

public class MyMapper {

        MyMapper() {
            Object someObject = new Person();
            final var listOfObjects = List.of(someObject);
            final var listOfPerson = toListOfPerson(listOfObjects);
        }

        List<Optional<Person>> toListOfPerson(Object object) {
            return ((List) object).stream()
                           .map(this::toPerson)
                           .collect(Collectors.toList());
        }

        Optional<Person> toPerson(Object object) {

            if (object instanceof Person) {
                return Optional.of((Person) object);
            }
            return Optional.empty();
        }

        public class Person {}
}

2 Answers 2

3

cast it to a typed List and then your .map(this::toPerson) will "accept" the element of this List

List<Optional<Person>> toListOfPerson(Object object) {
            return ((List<Object>) object).stream()
                    .map(this::toPerson)
                    .collect(Collectors.toList());
    }
Sign up to request clarification or add additional context in comments.

Comments

1

It doesn't make sense to keep the collection of optional objects which could be potentially empty, it almost the same as storing null values.

If you think for some reason that List<Optional<Person>> is good idea, I recommend you to have a look at this question. A quote from the answer by Stuart Marks (JDK developer):

I'm sure somebody could come up with some contrived cases where they really want to store an Optional in a field or a collection, but in general, it is best to avoid doing this.

Optional was introduced in the JDK as a limited mechanism to represent a nullable return value. That is its only purpose, other cases of usage of optional objects like optional method arguments, fields, collections of optionals are considered to be antipattern.

You have to unpack your Optionals in the stream:

public List<Person> toListOfPerson(Object object) {
    if (!(object instanceof List<?>)) {
        return Collections.emptyList();
    }
    
    return ((List<?>) object).stream()
        .map(this::toPerson)
        .filter(Optional::isPresent)
        .map(Optional::get)
        .collect(Collectors.toList());
}

2 Comments

thanks for the detailed answer. In my use case there is an extra treatment of "persons" that do not exist. But I agree to your comment and just throw a class cast exception instead of using optionales.
@TheBohne extra treatment of "persons" that do not exist - Then you might consider implementing the Null-object pattern to handle this case. Also note that instanceof checks is a code-smell. If you are working with a legacy system that returns you objects, that understandable. But it better to avoid it and design the logic in such a way that you don't need type-checks and down-casting.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.