Search code examples
javajava-streamjava-14

Convert one Optional<List<Object>> to another Optional<List<Object>> in Java


How can I convert Optional List object from one type to another, for an example

Optional<List<ProductMultipleOptionViewModel>> productOptionType1 // One type

Optional<List<ProductMultipleOption>> productOptionType2 // Other type

ProductMultipleOptionViewModel

Type 1

@Introspected
public record ProductMultipleOptionViewModel(
        ProductOptionViewModel productOption,
        String optionName) {
}

Type 2

 @Introspected
    public record ProductMultipleOption(
            ProductOptionViewModel productOption,
            String optionName) {
    }

I want to convert from Optional<List<ProductMultipleOption>>to other Optional<List<ProductMultipleOptionViewModel>>. I tried the below code

Optional<List<ProductMultipleOptionViewModel>> conveertItem = Optional.ofNullable(product.getProductMultipleOption())
                .orElseGet(null)
                .stream()
                .map(option -> {
                    return new ProductMultipleOptionViewModel(
                            ProductOptionViewModel.valueOf(//Access the option value//), //access the option value//
                    );
                })
                .collect(Collectors.toList());

With the above code, I am not able to access the option value inside map method

If product.getProductMultipleOption() is null return null or empty list.


Solution

  • You should rarely use Optional and Collections (like List or Set) together. Instead you should work with empty Collections. Also keep in mind that Optionals should not really be used for conditional logic, but rather as return values.

    Either using a normal if statement:

    List<ProductMultipleOptionViewModel> conveertItem = new ArrayList<>(); 
    if (product.getProductMultipleOption() != null) {
       for(ProductMultipleOption option : product.getProductMultipleOption()) {
           conveertItem.add(new ProductMultipleOptionViewModel(
              ProductOptionViewModel.valueOf(option)
           ));
       }
    }
    

    Another variant:

    List<ProductMultipleOption> list = product.getProductMultipleOption();
    if (list == null) {
        list = Collections.emptyList();
    }
    List<ProductMultipleOptionViewModel> conveertItem = new ArrayList<>(); 
    for(ProductMultipleOption option : list) {
       conveertItem.add(new ProductMultipleOptionViewModel(
          ProductOptionViewModel.valueOf(option)
       ));
    }
    
    

    Or if you really want to use Streams and Optionals (matter of taste):

    List<ProductMultipleOptionViewModel> conveertItem = Optional.ofNullable(product.getProductMultipleOption())
        .map(List::stream)
        .orElseGet(Stream::empty)
        .map(option -> new ProductMultipleOptionViewModel(
            ProductOptionViewModel.valueOf(option)
        ))
        .collect(Collectors.toList());
    

    Now that's a lot of code for simply converting a nullable List. So why not return an empty List in the first place? Change product.getProductMultipleOption() to something like this:

    public List<ProductMultipleOption> getProductMultipleOption() {
        List<ProductMultipleOption> list = ...; // your current logic for getting the list
        return list == null ? Collections.emptyList() : list;
    }
    

    That way you never have to worry about null checks. Because you're simply working with an empty collection wherever you're calling getProductMultipleOption().