除非指定参数类型,否则 Java 无法编译通用 lambda 参数

标签 java generics type-inference

在测试时,我将我的 Junit 升级到 5.0(因此用新版本替换了我的一些 assertTrue() 方法)。这样做之后,我发现我的一个测试没有编译。我将问题简化为没有 junit 或其他依赖项的普通旧 java。结果是以下无法编译的代码:

  public static void recreate() {
    // This does NOT work
    Recreation.assertTrue(identity((x) -> Boolean.TRUE)); 
    // This DOES work
    Recreation.assertTrue(identity((String x) -> Boolean.TRUE)); 
  }

  private static class Recreation {
    public static void assertTrue(boolean b) {
      System.out.println("boolean argument: " + b);
    }

    // If this method is removed, the code will compile. 
    public static void assertTrue(Supplier<Boolean> booleanSupplier) {
      System.out.println("supplier argument: " + booleanSupplier.toString());
    }
  }

  private static <K> K identity(Function<String, K> function) {
    return function.apply("hello");
  }

如上例所示,如果满足以下任一条件,代码将通过编译:

  1. 指定lambda参数类型

  2. 重载的 assertTrue(Supplier booleanSupplier) 方法被移除

这是类型推断/删除的问题,还是这里可能发生了什么?

构建错误:

Error:(10, 35) incompatible types: inference variable K has incompatible bounds
    lower bounds: java.util.function.Supplier<java.lang.Boolean>,java.lang.Object
    lower bounds: java.lang.Boolean

规范:

openjdk version "11.0.1" 2018-10-16
OpenJDK Runtime Environment (build 11.0.1+13-Ubuntu-3ubuntu114.04ppa1)
OpenJDK 64-Bit Server VM (build 11.0.1+13-Ubuntu-3ubuntu114.04ppa1, mixed mode, sharing)

OS: Ubuntu 14.04.5 LTS

编辑:确认问题也存在于 Java 8 上:

java version "1.8.0_31"
Java(TM) SE Runtime Environment (build 1.8.0_31-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode)
exit status 1
Main.java:10: error: incompatible types: inferred type does not conform to upper bound(s)
    Recreation.assertTrue(identity((x) -> Boolean.TRUE));
                                  ^
    inferred: Boolean
    upper bound(s): Supplier<Boolean>,Object
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
1 error

最佳答案

环顾四周并阅读此处的 Java 语言规范后 https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.1

我认为这里有两个步骤:

首先,重载解析无法推断出 identity((x) -> Boolean.TRUE) 的类型因为它是隐式的 lambda,我认为为了简单起见,它没有被考虑在内。因此,它将扩大参数搜索和使用 public static void assertTrue(Supplier<Boolean> booleanSupplier) .

其次,重载解析完成后,类型推断 开始。这次它真正检查了推断类型,即 Boolean。 , 因为它与 Supplier<Boolean> booleanSupplier 不兼容,你会得到编译错误。

和之前的回答一样,有解决办法,

例如

Recreation.assertTrue(identity((x) -> () -> Boolean.TRUE));

我在这里找到了很好的解释:Java8: ambiguity with lambdas and overloaded methods

关于除非指定参数类型,否则 Java 无法编译通用 lambda 参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55483445/

相关文章:

java - 将纹理绘制到帧缓冲区会扭曲纹理

java - fragment 已添加到 Activity 但不显示

java - Atlassian stash 相当于 git 管理器(开源)

符合协议(protocol)可变子集的 Swift 泛型类型

generics - 使用Any时模棱两可的重载方法引用

c++ - 如何将 Boost.MultiArray 的二维 View 作为函数的参数?

c++ - g++ 和 clang++ 推断函数模板返回类型的不同行为

java - 在 Eclipse RCP 4 中打开一个编辑部分(作为以前 eclipse 版本中的编辑器)

ios - 未指定数据类型时 Swift 变量为 nil

java - Java中未知类型的排序列表