Дженерики: Ошибка преобразования / каста Upper Bounded Wildcards - PullRequest
0 голосов
/ 08 мая 2018

Вот тест:

@Test
public void genericsTest() {
    String s = "x";
    List<? extends String> strings = singletonList(s);
    Optional<Function<String, List<? extends String>>> optionalSelector = Optional.empty();
    optionalSelector.map(selector -> selector.apply(s)).orElse(strings);
}

А вот и ошибка:

java: incompatible types: java.util.List<capture#1 of ? extends java.lang.String> cannot be converted to java.util.List<capture#2 of ? extends java.lang.String>

Конечно, это можно исправить с помощью трюка необработанного типа.

    optionalSelector.map(selector -> selector.apply(s)).orElse((List)strings);

Но я бы хотел понять причину этой ошибки.

Обновление

Это не дубликат Списки с подстановочными знаками вызывают общую ошибку вуду , которая о добавлении в верхний ограниченный список подстановочных знаков; эта тема также хорошо описана в Правилах подстановочных знаков .

В моем случае я не пытаюсь добавить что-то в список, как вы можете видеть выше.

Также сообщения об ошибках различны.

1 Ответ

0 голосов
/ 08 мая 2018

Кажется, что-то не так с множественными уровнями вывода типов здесь. С этой строкой:

optionalSelector.map(selector -> selector.apply(s)).orElse(strings);

При вызове map вы пытаетесь преобразовать исходный Optional<Function<String, List<? extends String>>> в Optional<List<? extends String>>. Затем, с orElse, вы пытаетесь преобразовать это в List<? extends String>. Это кажется разумным.

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

Optional<List<? extends String>> intermedSelector = optionalSelector.map(selector -> selector.apply(s));
intermedSelector.orElse(strings);

Это показывает, что если компилятор может вывести результат вызова на map как Optional<List<? extends String>>, то вызов на orElse скомпилируется.

Тем не менее, я могу изменить явный тип на что-то, где объявление intermedSelector компилируется, но вызов orElse не компилируется. Я изменил явный тип interMedSelector с Optional<List<? extends String>> на Optional<? extends List<? extends String>> с добавлением ? extends.

Optional<? extends List<? extends String>> intermedSelector = optionalSelector.map(selector -> selector.apply(s));
intermedSelector.orElse(strings);

Ошибка здесь похожа на ту, которую вы получаете:

J.java:26: error: incompatible types: List<CAP#1> cannot be converted to CAP#2
    List<? extends String> result = intermedSelector.orElse(strings);
                                                            ^
  where CAP#1,CAP#2 are fresh type-variables:
    CAP#1 extends String from capture of ? extends String
    CAP#2 extends List<? extends String> from capture of ? extends List<? extends String>

С вашим кодом компилятор должен вывести что-то неожиданное, но связанное с Optional<List<? extends String>>. Я не могу сказать, является ли перехваченный тип компилятора Optional<? extends List<? extends String>> или что-то еще, но, похоже, именно поэтому вы получаете ошибку.

Мое решение

Optional<List<? extends String>> intermedSelector = optionalSelector.map(selector -> selector.apply(s));
intermedSelector.orElse(strings);

может быть выражено в одном выражении, если я предоставляю правильный параметр типа для вызова map, чтобы компилятор не выводил что-то неожиданное.

optionalSelector.<List<? extends String>>map(selector -> selector.apply(s)).orElse(strings);
...