【问题标题】:Eclipse or Javac bug; lambda type inferenceEclipse 或 Javac 错误; lambda 类型推断
【发布时间】:2017-02-08 12:51:00
【问题描述】:

以下代码使用 javac 和 Eclipse 4.6.1/4.6 编译,但在 Eclipse 4.6.2 中产生错误:

package ecbug;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Foo
{
    class A
    {
        public int getStart() { return 0; }
    }

    void someMethod(List<A> toRemove)
    {
        Collections.sort(toRemove, Comparator.comparing(t -> -t.getStart()));
    }
}

Eclipse 4.6.2 在-t.getStart() 下抱怨类型不匹配:无法从 int 转换为 Comparable>.

我认为Comparator.comparing(...) 的参数应该是Comparable&lt;T&gt;T = A,函数方法compareTo 返回int。 Eclipse 似乎认为 lambda 函数应该返回 Comparable>

我强烈怀疑一个 Eclipse 错误,但肯定有 Eclipse 正确实现了语言规范而 javac 没有的情况,所以似乎值得一问:这是一个 Eclipse 错误还是 javac 错误?那里有任何语言律师可以指出语言规范的相关部分吗?

可能相关的问题,在我看来不是重复的:

Java 8 Stream flatMap and group by code compiler error - 类似的错误信息,但不清楚是否是完全相同的问题; Answer 声称这是一个 Eclipse 错误,但没有提供错误链接或 JLS 引用;指的是旧的 Eclipse 版本。

Why didn't this java 8 example using type inference compile in Eclipse? - 与上一个类似

Java Stream collect after flatMap returns List<Object> instead of List<String> - 再次,可能是一个不同的问题; cmets 声称存在 Eclipse 问题,但没有通过引用 JLS 来证明其合理性,也没有提供指向 Eclipse 错误报告的链接。

【问题讨论】:

  • 在过去的几年里,我看到过几个这样的问题。您是否进行了彻底的搜索?
  • @T.J.Crowder 很难为如此具体的内容找到正确的搜索词,但我确实搜索了一段时间,是的。有些问题可能会显示相关错误(例如stackoverflow.com/questions/25853988/…),但我找不到任何与语言规范相关的问题,这就是我在这里所要求的。
  • 对我来说编译没有错误(Eclipse 4.6)
  • @ThomasFritsch 实际上,它似乎随着最新的 Eclipse JDT 发生了变化:版本 3.12.2.v20161124-1400 - 这是 4.6.2 更新的一部分(4.6.1 和 4.6 编译代码成功地)。不过,根据语言规范,哪种行为是正确的仍然是一个悬而未决的问题。
  • 我已经提交了一个 Eclipse 错误 - bugs.eclipse.org/bugs/show_bug.cgi?id=511924 - 看看开发人员怎么说。

标签: java eclipse lambda language-lawyer


【解决方案1】:

你的解释没有成功。 Comparator.comparing(...)(单参数版本)的参数不应该是Comparable&lt;T&gt;,而是Function&lt;? super T,? extends U&gt;,而T := A,但UU extends Comparable&lt;? super U&gt;

所以当你说

Eclipse 似乎认为 lambda 函数应该返回 Comparable&lt;? super Comparable&lt;? super U&gt;&gt;

您对 Eclipse 的期望是正确的,而 Eclipse 的期望也是正确的。

但是您的函数返回一个应该比较的int 值,当您将该值装箱到Integer 时,您有一个满足预期U extends Comparable&lt;? super U&gt; 约束的类型,因为Integer 实现Comparable&lt;Integer&gt; .换句话说,U 应该被推断为Integer,但显然,由于需要将int 装箱到Integer,这个特定的 Eclipse 版本无法做到这一点。


附带说明,当您想比较int 属性时,您可能还是想使用Comparator.comparingInt(...)。使用这个工厂,返回的比较器完全避免了 intInteger 的装箱。

此外,您不应该通过否定来反转整数排序。问题在于
-Integer.MIN_VALUE == Integer.MIN_VALUE,因为试图否定可能的最小int 值会导致溢出再次评估为最小的int 值,而不是最大的值。使用否定来颠倒顺序可能在很多情况下都有效,在某些情况下它可能是合理的,因为可以排除这个特殊值,但是,它会产生一个坏习惯,在可能发生这种情况的情况下可能会适得其反,当然,很少,通常只在客户那里......

正确的成语应该是

Collections.sort(toRemove, Comparator.comparingInt(A::getStart).reversed());

通过交换两个元素进行比较来工作,这在所有场景中都有效并且没有性能缺陷。

【讨论】:

  • 是的,我对Comparator.comparing(...) 的参数类型感到困惑。这一切听起来都是正确的,但在我接受之前,我会等待关于 Eclipse 错误的响应;谢谢。关于comparingInt的使用,我知道这一点,但是测试用例的原始代码不是我的代码。
  • 它被标记为另一个错误的副本 - bugs.eclipse.org/bugs/show_bug.cgi?id=510111 - 那里的 cmets 很有趣,例如“...凡提到 Fi θ,javac 首先执行未指定的替换:所有推理变量已经实例化的被它们各自的实例化替换。” “... JDK-8052325 要求我们生成进一步的约束。缺乏此类约束是导致此错误中的示例失败的原因。” (bugs.openjdk.java.net/browse/JDK-8052325)。似乎存在一些规格问题。无论如何都接受你的回答。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多