gpt4 book ai didi

java - 为什么 Java 编译器不能从约束 Iterable< 中推断出 Iterable?扩展 CharSequence> 和 () -> (Iterator)

转载 作者:行者123 更新时间:2023-12-04 15:28:56 26 4
gpt4 key购买 nike

背景:我最近写了an answer我建议编写以下代码:

Files.write(Paths.get("PostgradStudent.csv"),
Arrays.stream(PGstudentArray).map(Object::toString).collect(Collectors.toList()),
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);

想了想,我说:“我这里其实不需要列表,我只需要一个 Iterable<? extends CharSequence>”。
Stream<T>有一个方法 Iterator<T> iterator() ,然后我想,嗯,这很容易:
Iterable<? extends CharSequence> iterable = () -> Arrays.stream(arr).map(Object::toString).iterator();

(我将它提取到这个问题的局部变量中,我想在最后内联。)
不幸的是,如果没有额外的类型提示,这将无法编译:
error: incompatible types: bad return type in lambda expression
Iterable<? extends CharSequence> iterable = () -> Arrays.stream(arr).map(Object::toString).iterator();
^
Iterator<String> cannot be converted to Iterator<CharSequence>

当然,添加一些类型提示将使这项工作:
Iterable<? extends CharSequence> iterable2 = (Iterable<String>) () -> Arrays.stream(arr).map(Object::toString).iterator();
Iterable<? extends CharSequence> iterable3 = () -> Arrays.stream(arr).<CharSequence>map(Object::toString).iterator();

在我的理解中,Java 编译器做了以下事情:
  • 它查看表达式的目标类型,即 Iterable<? extends CharSequence> .
  • 然后判断这个接口(interface)的函数类型,即() -> Iterator<? extends CharSequence>就我而言。
  • 然后它查看 lambda 并检查它是否兼容。
    就我而言,lambda 的类型为 () -> Iterator<String> .
    与步骤 2 中确定的函数类型兼容。

  • 有趣的是,如果我将 lambda 的目标更改为 Supplier :
    Supplier<Iterator<? extends CharSequence>> supplier = () -> Arrays.stream(arr)
    .map(Object::toString)
    .iterator();

    它会编译得很好。

    现在的问题是:为什么 javac 不能推断出这个 lambda 的正确类型?

    最佳答案

    在阅读了另一个答案(这是绝对正确的)和一些咖啡之后,似乎错误中的解释非常合乎逻辑。

    这里有两种情况:显式 lambda 类型和隐式 lambda 类型。显式类型是:

    Iterable<String> one = () -> Arrays.stream(arr).map(Object::toString).iterator();
    Iterable<? extends CharSequence> iterable = one;

    或在 OP 的示例中:
    Iterable<? extends CharSequence> iterable2 = (Iterable<String>) () -> Arrays.stream(arr).map(Object::toString).iterator();

    我们直接告诉编译器 lambda 表达式是哪种类型: Iterable<String> .

    在这种情况下,编译器只有一件事要做:查看目标是否可分配给该类型;很容易找到并且与 lambdas 本身没有太大关系。

    另一种类型是隐式类型,当编译器必须推断类型并且这里的事情变得有点棘手时。 “棘手”部分来自目标使用通配符这一事实,因此可以匹配多个选项。可能有无数种方法(当然是有限的,但只是为了证明一点)可以推断出 lambda。

    它可以从这样的东西开始,例如:
    Iterator<? extends Serializable> iter = Arrays.stream(arr).map(Object::toString).iterator();

    无论进一步做什么,这都会失败: CharSequence不延长 Serializable , 但是 String做;我们将无法分配 Iterable<? extends CharSequence> iterable到“无论推断类型与序列化是什么”。

    或者它可以从以下开始:
    Iterator<? extends Comparable<? extends CharSequence>> iter = Arrays.stream(arr).map(Object::toString).iterator();

    因此,理论上编译器可以开始推断该类型可能是什么,并一一检查“某些”推断类型是否与目标匹配;但显然需要大量工作;因此没有完成。

    另一种方法更容易,“切割”目标,从而将推理的可能性降低到一个。一旦目标转换为:
    Iterable<CharSequence> iterable...

    编译器要做的工作要简单得多。

    顺便说一句,这不是我第一次在 lambdas 中看到这种隐式与显式类型的逻辑。

    关于java - 为什么 Java 编译器不能从约束 Iterable< 中推断出 Iterable<String>?扩展 CharSequence> 和 () -> (Iterator<String>),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58538083/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com