泛型:上限有界通配符转换/投射错误

时间:2018-05-07 22:34:05

标签: java generics functional-programming

以下是测试:

@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);

但我想了解这个错误的原因。

更新

它不是Lists with wildcards cause Generic voodoo error的副本,它是关于添加到上限通配符列表中的; Wildcard Guidelines中也详细描述了这个主题。

在我的情况下,我没有尝试在列表中添加内容,如上所示。

此外,错误消息也不同。

1 个答案:

答案 0 :(得分:2)

这里的多级类型推断似乎有问题。有了这条线:

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);