当前位置: 首页 > 知识库问答 >
问题:

为什么Java编译器不能从约束Iterable推断Iterable?扩展CharSequence>和()->(迭代器)

章城
2023-03-14

背景:我最近写了一个答案,建议编写以下代码:

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

经过一番思考,我说:“我实际上不需要这里的列表,我只需要一个<代码>Iterable

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编译器执行以下操作:

  1. 它查看表达式的目标类型,它是可编辑的

有趣的是,如果我将lambda的目标更改为供应商

Supplier<Iterator<? extends CharSequence>> supplier = () -> Arrays.stream(arr)
    .map(Object::toString)
    .iterator();

它会很好地编译。

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

共有2个答案

盖斌
2023-03-14

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

这里有两种情况:显式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

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

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

它可以从这样的东西开始,例如:

Iterator<? extends Serializable> iter = Arrays.stream(arr).map(Object::toString).iterator();

无论进一步做什么,这都将失败:CharSequence不会扩展可序列化的,但会扩展字符串;我们将无法分配Iterable

或者它可以从:

Iterator<? extends Comparable<? extends CharSequence>> iter = Arrays.stream(arr).map(Object::toString).iterator();

因此,理论上,编译器可以开始推断该类型,并逐一检查“特定”推断类型是否与目标匹配;但显然需要大量的工作;因此,没有这样做。

另一种方法要简单得多,“切掉”目标,从而将推理的可能性降到一个。一旦目标转换为:

Iterable<CharSequence> iterable...

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

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

仉嘉泽
2023-03-14

您可以在此处找到一些解释:

在检查兼容性之前,必须将通配符参数化函数接口类型转换为函数类型(方法签名)。。。其工作原理如下:

Iterable<? extends CharSequence> becomes () -> Iterator<CharSequence>

因此,如果lambda表达式是隐式类型的,则LHS成为迭代器

Iterator<String> cannot be converted to Iterator<CharSequence>

JLS§18.5.3中也解释了这种行为。

 类似资料:
  • 我正在阅读有效的java,有一个问题。我不理解为什么流迭代器返回Iterable。据我所知,Iterable包含迭代器接口。但在流api中,即使迭代器没有继承Iterable,这段代码仍在工作。 我对这些代码很困惑。因为迭代器和Iterable之间没有关系,除了Iterable有迭代器。

  • 什么是迭代? 例子: for a in b.value(): pass for x, y in de.items(): pass 字符串也可以 for a in 'abcv': pass 如何判断是否可以迭代 请看代码 from collections import Iterable isinstance('abc', Iterable) True 这里就引出了

  • 问题内容: Python中的最基本定义是什么? 我已经阅读了多个定义,但是我无法确定确切的含义,因为它仍然不会陷入。 有人可以在外行方面为我提供3个定义的帮助吗? 问题答案: 迭代是一个通用的术语,用于一个接一个地获取某物的每一项。无论何时使用显式或隐式循环遍历一组项,即迭代。 在Python中,和有特定的含义。 是一个具有返回迭代器的iter方法的对象,或者定义一个可以从零开始获取顺序索引的方法

  • 问题内容: 使用Java5,我们可以编写: 或仅在for循环中使用Iterable。这非常方便。 但是,您不能像这样编写可迭代的通用方法: 并使用数组调用它,因为它不是Iterable: 我想知道这个设计决定背后的原因。 问题答案: 数组可以实现接口(和)。那为什么不呢?我猜想Iterable强制添加方法,而数组不实现方法。甚至不覆盖。无论如何,应将引用数组视为不理想的-使用。如dfa注释所示,将

  • 可迭代(Iterable) 对象是数组的泛化。这个概念是说任何对象都可以被定制为可在 for..of 循环中使用的对象。 数组是可迭代的。但不仅仅是数组。很多其他内建对象也都是可迭代的。例如字符串也是可迭代的。 如果从技术上讲,对象不是数组,而是表示某物的集合(列表,集合),for..of 是一个能够遍历它的很好的语法,因此,让我们来看看如何使其发挥作用。 Symbol.iterator 通过自己

  • 我有这个(看起来)无辜的代码(这里简化成这个JUnit测试用例): 问题在于行:Eclipse编译器在这里没有发现问题(我也是),很高兴编译并运行测试并成功。 但是,当我在命令行上编译它时(在这种情况下是Maven 3,Oracle JDK8,但也不能直接与javac一起使用),会抛出一个编译错误: 我认为从返回类型(< code>Map 这是JDK的错误还是Eclipse编译器的错误还是别的什么