Лямбда внутри orElseGet в обобщенных шаблонах - PullRequest
3 голосов
/ 05 марта 2020

Часть моего кода выглядит следующим образом:

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

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

Существует перечисление , с помощью которого я получаю класс обслуживания с использованием метода stati c из entityServiceFactory, Затем я вызываю метод getFooArgs (...) , который возвращает Необязательный . Затем я хотел бы отобразить его с помощью метода EntityService или использовать метод без аргументов, чтобы получить значение по умолчанию, когда getFooArgs возвращает пустое значение Необязательно.

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

К сожалению, я получаю Bad return type in lambda expression: List<capture of ? extends AbstractEntityDto> cannot be converted to List<capture of ? extends AbstractEntityDto> из моей IDE. Что мне нужно сделать, это:

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

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

Можете ли вы объяснить, почему это происходит? Я предполагаю, что это как-то связано с ? и дженериками, но я подумал, что при использовании ? многие ограничения типа исчезнут. Если кода слишком мало, чтобы понять это, пожалуйста, дайте мне знать.

1 Ответ

3 голосов
/ 05 марта 2020

Я даже не пытаюсь выяснить, почему компилятор не принимает эту конструкцию. Когда дело доходит до вывода типов и подстановочных знаков, многие вещи, которые выглядят так, как будто они должны работать, не делают.

Самое простое решение - это предоставить явный тип для общего вызова c вызова map:

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

Как правило, следует избегать подстановочных знаков в типах возврата . Нет смысла носить с собой подстановочный знак. Напротив, когда у вас действительно есть код, работающий с List<ConcreteEntityDto>, и вам нужно передать его методу, который должен быть в состоянии справиться с большим количеством случаев, объявив тип параметра с подстановочным знаком, например List<? extends AbstractEntityDto>, принять List<ConcreteEntityDto>, а также List<SomeOtherEntityDto>, очень полезно.

Обратите внимание, что при

List<? extends AbstractEntityDto> list = …

вы можете сделать

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

для последующей обработки который не пытается изменить список. Оболочка, возвращаемая unmodifiableList, предотвращает любые попытки вставить элементы в список неизвестных подтипов AbstractEntityDto, но может гарантировать возвращение экземпляров AbstractEntityDto для всех методов запроса. Вот почему это изменение типа действительно.

...