Lambda inside orElseGet in bounded wildcard generics

ogarogar :

Part of my code looks like this:

EntityTypeEnum entityType = EntityTypeEnum.fromName(entityTypeName).orElseThrow(...);
EntityService<? extends AbstractEntityDto> entityService = entityServiceFactory.getEntityService(importType);

return getFooArgs(bar)
    .map(entityService::getListWithFooArgs)
    .orElseGet(()->entityService.getListNoArgs());

There is an enum by which I obtain service class using static method from entityServiceFactory. Then I invoke method getFooArgs(...), that returns Optional< FooArg >. Then I'd like to map it using EntityService method or use a method without args to obtain "default" value, when getFooArgs return empty Optional.

public interface EntityService<T extends AbstractEntityDto> {
    List<T> getListNoArgs();
    List<T> getListWithFooArgs(FooArg fooArgs);
}

Sadly I get Bad return type in lambda expression: List<capture of ? extends AbstractEntityDto> cannot be converted to List<capture of ? extends AbstractEntityDto> from my IDE. What I have to do is:

Optional<List<? extends AbstractEntityDto>> listOptional = getFooArgs(bar)
        .map(entityService::getListWithFooArgs);

if(listOptional.isPresent())
    return listOptional.get();
else
    return entityService.getListNoArgs();

Can you explain why does it happen? I assume it's soemthing related to ? and generics, but I thought that by using ? a lot of type's restrictions would disappear. If there is too little of code to figure it out, please let me know.

Holger :

I do not even try to find out why the compiler does not accept this construct. When it comes to type inference and wildcards, lots of things that look like they should work, don’t.

The easiest fix is to provide an explicit type for the generic invocation of map:

return getFooArgs(bar)
    .<List<? extends AbstractEntityDto>>map(entityService::getListWithFooArgs)
    .orElseGet(entityService::getListNoArgs);

Generally, you should avoid wildcards in return types. There is no value in carrying the wildcard around. In contrast, when you truly have code dealing with List<ConcreteEntityDto> and need to pass it to a method which should be able to deal with more cases, declaring a parameter type with a wildcard, like List<? extends AbstractEntityDto>, to accept the List<ConcreteEntityDto> and also List<SomeOtherEntityDto>, is quiet useful.

Note that given a

List<? extends AbstractEntityDto> list = …

you can do

List<AbstractEntityDto> view = Collections.unmodifiableList(list);

for subsequent processing which doesn’t attempt to modify the list. The wrapper returned by unmodifiableList prevents any attempt to insert elements into the list of unknown subtype of AbstractEntityDto but can guaranty to return instances of AbstractEntityDto for all query methods. That’s why this type change is valid.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=26784&siteId=1