Java Stream Generics Type Mismatch

Chr*_*kes 14 java eclipse java-8 java-stream

在操作Java 8流时,我遇到了一个错误,编译器似乎"忘记"了我的泛型参数的类型.

以下代码段创建了一个类名流,并尝试将流映射到流的流Class<? extends CharSequence>.

public static Stream<Class<? extends CharSequence>> getClasses() {

    return Arrays.asList("java.lang.String", "java.lang.StringBuilder", "Kaboom!")
        .stream()
        .map(x -> {
            try {
                Class<?> result = Class.forName(x);

                return result == null ? null : result.asSubclass(CharSequence.class);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            return null;
        })
        //.filter(x -> x != null)
        ;

}
Run Code Online (Sandbox Code Playgroud)

当我取消注释过滤器以从流中删除空条目时,我得到编译错误

类型不匹配:无法转换为Class <capture#15-of?将CharSequence>扩展为Class <Object>

有人可以向我解释为什么添加过滤器导致此错误?

PS:这里的代码有些随意,很容易让错误消失:在应用过滤器之前将映射流分配给临时变量.我感兴趣的是为什么上面的代码片段会产生编译时错误.

编辑:正如@Holger所指出的那样,这个问题并不完全与Java 8 Streams重复:为什么Collectors.toMap对于带有通配符的泛型有不同的行为?因为那里有问题的片段目前编译没有问题,而这里的片段没有.

pde*_*dem 1

这是因为类型推断:

该类型是从它的目标“猜测”出来的:我们知道 map(anything) 必须返回 a, "Stream<Class<? extends CharSequence>>"因为它是函数的返回类型。如果您将其返回到另一个操作(例如过滤器或映射),我们就会失去这种类型推断(它无法“通过”链接)

类型推断有其局限性,你会发现它。

解决方案很简单:您是否说过,如果使用变量,则可以指定目标然后帮助类型推断。

这个编译:

public static Stream<Class<? extends CharSequence>> getClasses() {
Stream<Class<? extends CharSequence>> map1 = Arrays.asList ("java.lang.String", "java.lang.StringBuilder", "Kaboom!").stream ().map (x -> {
  try {
    Class<?> result = Class.forName (x);
    return result == null ? null : result.asSubclass(CharSequence.class);
  } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace ();
  }

  return null;
});
return map1.filter(x -> x != null);
Run Code Online (Sandbox Code Playgroud)

请注意,我修改了代码以始终返回 null,以表明推断的类型并非来自 lambda 返回类型。

我们看到map1的类型是通过变量声明(它的目标)推断出来的。如果我们返回它,它是等价的,目标是返回类型,但是如果我们链接它:

这不能编译:

public static Stream<Class<? extends CharSequence>> getClasses () {

return Arrays.asList ("java.lang.String", "java.lang.StringBuilder", "Kaboom!").stream ().map (x -> {
  try {
    Class<?> result = Class.forName (x);
    return result == null ? null : result.asSubclass(CharSequence.class);
  } catch (Exception e) {

    e.printStackTrace ();
  }

  return null;
}).filter(x -> x != null);
Run Code Online (Sandbox Code Playgroud)

第一个映射声明没有目标,因此默认定义推断类型: Stream<Object>

编辑

另一种使其工作的方法是使类型推断与 Lambda 返回值(而不是目标)一起工作,例如,您需要使用强制转换来指定返回类型。这将编译:

public static Stream<Class<? extends CharSequence>> getClasses2 () {

return Arrays.asList ("java.lang.String", "java.lang.StringBuilder", "Kaboom!").stream ().map (x -> {
  try {
    Class<?> result = Class.forName (x);
     return (Class<? extends CharSequence>)( result == null ? null : result.asSubclass(CharSequence.class));
  } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace ();
  }

  return (Class<? extends CharSequence>)null;
}).filter(x -> x != null);
Run Code Online (Sandbox Code Playgroud)

}

请注意,这是因为操作链,您可以用 map(x->x) 替换 .filter(x -> x != null) ,您也会遇到同样的问题。

编辑:修改示例以完全匹配问题。