首页 > 解决方案 > 代码在 Eclipse 中编译,但在 javac 中编译:带有功能子接口的 curried lambdas。哪个是对的?

问题描述

我在 Eclipse 中开发了一些代码,测试成功,将其推送到我们的 Jenkins CI 服务器,并收到一封电子邮件,称 Maven 因 Java 编译错误而窒息。我随后隔离了该问题并创建了以下显示该问题的最小示例:

import java.util.List;
import java.util.function.Function;

class MinimalTypeFailureExample {
    public static void main(String[] args) {
        List<String> originalList = null;  // irrelevant
        List<IntToByteFunction> resultList = transform(originalList,
                outer -> inner -> doStuff(inner, outer));
        System.out.println(resultList);
    }

    static <F, T> List<T> transform(List<F> originalList,
            MyFunction<? super F, ? extends T> function) {
        return null;  // irrelevant
    }

    static Byte doStuff(Integer inner, String outer) {
        return null;  // irrelevant
    }
}

@FunctionalInterface
interface MyFunction<F, T> extends Function<F, T> {
    @Override
    T apply(F input);
}

@FunctionalInterface
interface IntToByteFunction {
    Byte applyIntToByte(Integer inner);
}

在 Eclipse 中,此代码编译没有错误,并且似乎按预期执行。但是,使用 javac 编译会出现以下错误:

MinimalTypeFailureExample.java:7: error: incompatible types: cannot infer type-variable(s) F,T
                List<IntToByteFunction> resultList = transform(originalList, outer -> inner -> doStuff(inner, outer));
                                                              ^
    (argument mismatch; bad return type in lambda expression
      T is not a functional interface)
  where F,T are type-variables:
    F extends Object declared in method <F,T>transform(List<F>,MyFunction<F,? extends T>)
    T extends Object declared in method <F,T>transform(List<F>,MyFunction<F,? extends T>)
1 error

transform()将from的参数类型更改MyFunctionFunction,或删除参数类型中的通配符? extends,会使示例代码在 javac 中编译。

显然,Eclipse 或 javac 都违反了 Java 语言规范。问题是,我是在 Eclipse 还是 javac 上提交错误报告?泛型 lambda 的类型推断规则非常复杂,以至于根据 JLS,我不知道这个程序是否是合法的 Java。

动机说明

在原始代码中,transform()是 Guava 的com.google.common.collect.Lists.transform(). 该MyFunction接口是 Guava 的com.google.common.base.Function接口,由于历史原因而扩展java.util.function.Function

这段代码的目的是创建一个第一种类型列表的视图作为第二种类型的列表。第二种类型是函数式接口类型,我想使用基于输入列表中的值构造的这种类型的函数来填充输出列表——因此是柯里化 lambda 表达式。

可重复性的版本信息

测试的 Eclipse 版本:

  • 2018-09 (4.9.0) 版本号:20180917-1800
  • 2019-03 RC1 (4.11 RC1) 版本号:20190307-2044

测试的 javac 版本:

标签: javaeclipsegenericslambdafunctional-interface

解决方案


It looks like you run into JDK bug JDK-8156954 which has been fixed in Java 9 but not in Java 8.

It is a bug of Java 8 javac because in your example all variable types of the transform method can be inferred without violating the Java language specification as follows:

  • F: String (via first parameter originalList of type List<String>)
  • T: IntToByteFunction (via return type List<IntToByteFunction>)

These inferred variable types are compatible with the type of the second parameter, the chained lambda expression:

  • outer -> inner -> doStuff(inner, outer) resolves (with doStuff(Integer, String) to
  • String -> Integer -> doStuff(Integer, String) resolves to
  • String -> Integer -> Byte is compatible with
  • String -> IntToByteFunction is compatible with
  • MyFunction<? super String, ? extends IntToByteFunction>

Your example can be minimized further:

import java.util.function.Function;

class MinimalTypeFailureExample {

    void foo() {
        transform((Function<Integer, String>)null, o -> i -> {return "";});
    }

    <T, F> void transform(F f, MyFunction<T, ? extends F> m) {}

}

@FunctionalInterface
interface MyFunction<T, R> extends Function<T, R> {
    @Override
    R apply(T t);
}

MyFunction overrides the same with the same (R apply(T t);). If Function instead of MyFunction is used or if MyFunction extends Function but without @Override R apply(T t); then the error disappears. Also with F instead of ? extends F the error disappears.

Even if your example differs from the example in the mentioned bug, it can be assumed that it is the same bug because it is the only "argument mismatch; bad return type in lambda expression bug that has been fixed in Java 9 but not in Java 8 and that occurs only with lambda functions in combination with Java Generics.


推荐阅读