Несоответствие типов: невозможно преобразовать из спискак списку <FollowingResponseDto> - PullRequest
1 голос
/ 25 сентября 2019

У меня есть интерфейс IDto и FollowingResponseDto реализует IDto.У меня есть метод, который выглядит как таковой в классе под названием AbstractController:

protected final List<? extends IDto> findPaginatedAndSortedInternal(page, size, sortBy, sortOrder, uriBuilder, response){
    final Page<T> resultPage = getService().findAllPaginatedAndSortedRaw(page, size, sortBy, sortOrder);
    if (page > resultPage.getTotalPages()) {
        throw new ResourceNotFoundException();
    }

    return DtoEntityConverter.convertToResponseDtoList(Lists.newArrayList(resultPage.getContent()));
}

Это код для DtoEntityConverter.convertToResponseDtoList:

public static List<? extends IDto> convertToResponseDtoList(List<? extends IEntity> entities) {
    List<? extends IDto> dtoList = new ArrayList<>();
    entities.forEach(e -> e.convertToResponseDto());
    return dtoList;
}

Наконец, у меня есть класс, которыйextends AbstractController:

public class FollowingController extends AbstractReadOnlyController<Following> {
    @Override
@RequestMapping(method = RequestMethod.GET)
@ResponseBody
public List<FollowingResponseDto> findAllPaginatedAndSorted(
        final int page,
        final int size,
        final String sortBy,
         final String sortOrder,
        final UriComponentsBuilder uriBuilder, final HttpServletResponse response) {
        return findPaginatedAndSortedInternal(page, size, sortBy, sortOrder, uriBuilder, response);
    }
}

Вызов return findPaginatedAndSortedInternal(page, size, sortBy, sortOrder, uriBuilder, response); в FollowController выдает ошибку компиляции:

Type mismatch: cannot convert from List<capture#2-of ? extends IDto> to List<FollowingResponseDto>

Куда я иду не так?

Ответы [ 2 ]

0 голосов
/ 25 сентября 2019

Вы не можете просто присваивать или приводить ограниченные универсальные классы к их подтипу только с учетом параметра типа.Проще говоря, в Java, когда появился generic, была введена еще одна функция под названием Type Erasure .Generic позволяет выполнять строгую проверку типов во время компиляции, но не во время выполнения.Компилятор заменяет типы параметров типа фактическими типами, поэтому среда выполнения не знает о параметрах типа.

С type erasure претерпевает что-то подобное;если универсальный класс - что-то вроде этого;

public class Node<T> {

    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}

После компиляции класс станет чем-то похожим на это;(Примечание -> T тип здесь не связан)

public class Node {

    private Object data;
    private Node next;

    public Node(Object data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Object getData() { return data; }
    // ...
}

Если тип T связан, как в вашем случае;

public class Node<T extends Comparable<T>> {

    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}

станет примерно такимкласс после компиляции;

public class Node {

    private Comparable data;
    private Node next;

    public Node(Comparable data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Comparable getData() { return data; }
    // ...
}

То же самое происходит и с универсальными методами из-за стирания типа.Так что в вашем случае

protected final List<? extends IDto> findPaginatedAndSortedInternal(page, size, sortBy, sortOrder, uriBuilder, response){
    //...
}

стал таким во время компиляции;

protected final List<IDto> findPaginatedAndSortedInternal(page, size, sortBy, sortOrder, uriBuilder, response){
    //...
}

Теперь представьте, что в вашей функции public List<FollowingResponseDto> findAllPaginatedAndSorted(...) вы возвращаете тип экземпляра List<IDto>findPaginatedAndSortedInternal метод.Таким образом, его универсальный тип - это класс super класса FollowingResponseDto.Вот почему вы получаете эту ошибку компиляции.

Это поможет понять, что вы сделали не так.

0 голосов
/ 25 сентября 2019

Обратите внимание, что остается вопрос, почему ваши внутренние методы используют интерфейс, а открытый метод использует конкретный класс.Обычно это наоборот.Если это вообще возможно, я предлагаю изменить тип возвращаемого значения findAllPaginatedAndSorted на List<? extends IDto>.


Вы не можете разыграть с List<? extends IDto> до List<FollowingResponseDto>, поскольку первый может содержать другие типы, которыереализовать IDto, а не просто FollowingResponseDto.

Представьте себе следующий сценарий:

interface I {}
class A implements I {}
class B implements I {}

List<I> interfaceList = ...;
interfaceList.add(new A());
interfaceList.add(new B());
List<A> aList = interfaceList;  // error! interfaceList contains a B, and a B is not allowed in a list of As

Теперь вы можете утверждать, что в вашем сценарии нет B и что List<? extends IDto> содержит только экземпляры FollowingResponseDto.Но ваш компилятор этого не знает, и также нет гарантии, что это не изменится в будущем.

Чтобы это исправить, вам нужно выполнить преобразование самостоятельно.Либо сделайте промежуточное зло, брошенное в List<?>, либо создайте новое List<FollowingResponseDto> и добавьте каждый элемент из List<? extends IDto> по отдельности:

зло:

return (List<FollowingResponseDto>)(List<?>)findPaginatedAndSortedInternal(...);

не зло:

var idtoList = findPaginatedAndSortedInternal(...);
var followingResponseDtoList = new ArrayList<FollowingResponseDto>();
for (var idto : idtoList) {
    if (idto instanceof FollowingResponseDto)
        followingResponseDtoList.add((FollowingResponseDto)idto);
}
return followingResponseDtoList;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...