【问题标题】:Anonymous class replaced with lambda expressions匿名类替换为 lambda 表达式
【发布时间】:2013-11-29 14:06:56
【问题描述】:

我有使用 Java 8 new stream functionality 的示例代码(获取一系列 int 值 1 .. 20,跳过前 9 个,然后取其余 10 个,每个 int 值:减一并乘以 2)。

System.out.println(Arrays.toString(
    IntStream.rangeClosed(1, 20).skip(9).limit(10).map((new IntUnaryOperator() {
        @Override
        public int applyAsInt(int operand) {
            return operand - 1;
        }
    }).andThen(new IntUnaryOperator() {
        @Override
        public int applyAsInt(int operand) {
            return operand * 2;
        }
    })).toArray()));

输出如下:

[18, 20, 22, 24, 26, 28, 30, 32, 34, 36]

现在我想用Lambda expressions 替换匿名类。以下转换工作正常(第二个匿名类替换为i -> i * 2 lambda 表达式),我得到相同的输出:

System.out.println(Arrays.toString(
    IntStream.rangeClosed(1, 20).skip(9).limit(10).map((new IntUnaryOperator() {
        @Override
        public int applyAsInt(int operand) {
            return operand - 1;
        }
    }).andThen(i -> i * 2)).toArray()));

但是,当我用 lambda 表达式替换第一个匿名类时:

System.out.println(
    Arrays.toString(
        IntStream.rangeClosed(1, 20).skip(9).limit(10)
            .map((v -> v - 1).andThen(i -> i * 2)).toArray()));

我无法编译我的代码。错误说Operator '-' cannot be applied to '<lambda parameter>', 'int'

你知道如何将两个 lambda 表达式与IntUnaryOperator.andThen 结合起来吗?

我知道我可以使用 ....limit(10).map(v -> v - 1).map(i -> i * 2).toArray() ...,它工作正常,但我想了解如何将 IntUnaryOperator.andThen 与 lambdas 一起使用(如果可能的话)。

【问题讨论】:

  • 有趣的问题!更正式的((int v) -> v - 1).andThen((int i) -> i * 2) 也不起作用:“错误:这里不需要 lambda 表达式”。
  • 编译器无法推断 (v -> v - 1).andThen(i -> i * 2) 的类型,因为它实际上必须进行 2 次推断,然后在这两次推断中进行三分之一。它很可能超出了类型系统的范围。

标签: java lambda anonymous-class java-8


【解决方案1】:

您必须将第一个 lambda 表达式显式转换为 IntUnaryOperator。以下代码有效:

System.out.println(
        Arrays.toString(
                IntStream.rangeClosed(1, 20).skip(9).limit(10)
                        .map(((IntUnaryOperator) v -> v - 1).andThen(i -> i * 2)).toArray()));

【讨论】:

  • 我认为有必要指出强制转换是必要的原因是 lambda 本身没有类型。它仅通过分配/转换接收类型。例如,表达式s -> s.isEmpty() 可以分配给java.util.function.Predicate<String>、java.util.function.Function<String,Boolean>、com.google.common.base.Predicate<String> 等。
【解决方案2】:

我设法将代码编译如下(尽管使用的是 eclipse 实验版本):

IntUnaryOperator first = v -> v - 1;
IntUnaryOperator second = i -> i * 2;
System.out.println(Arrays.toString(IntStream.rangeClosed(1, 20).skip(9)
                .limit(10).map(first.andThen(second)).toArray()));

我了解您希望能够在不声明 first 和 second 的情况下编写代码;但是,IntUnaryOperator 是 FunctionalInterface,根据 Java 编译器的工作方式,FunctionalInterface 需要有一个“上下文”才能按照您想要的方式进行组合。这就是为什么使用您的原始代码 sn-p Java 编译器无法将您的 lamda 表达式精确映射到 IntUnaryOperator 的实例的原因。

如需更长时间的讨论,请查看此主题:http://mail.openjdk.java.net/pipermail/jdk8-dev/2013-June/002668.html

【讨论】:

    【解决方案3】:

    另一种解决方案是使用静态IntUnaryOperator.identity() 作为起点:

    IntUnaryOperator.identity().andThen(v -> v - 1).andThen(i -> i * 2)
    

    使用静态导入,您也可以只使用 identity()!

    如果IntUnaryOperator 有一个静态方法,比如...

    static IntUnaryOperator first(IntUnaryOperator op) { return op; }
    

    ...我们可以简单地写:

    IntUnaryOperator.first(v -> v - 1).andThen(i -> i * 2)
    

    【讨论】:

    • 编写自己的静态first 方法很容易。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-24
    • 1970-01-01
    • 2016-10-11
    • 2013-04-24
    • 2011-09-22
    • 2014-01-28
    相关资源
    最近更新 更多