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

Java 8使用者/函数Lambda模糊性

拓拔耀
2023-03-14

我有一个重载方法,它分别接受一个使用者和一个函数对象,并返回一个与相应的使用者/函数匹配的泛型类型。我认为这很好,但是当我尝试用lambda表达式调用任何一个方法时,我得到一个错误,表明对该方法的引用是不明确的。

根据我对JLS§15.12的阅读。2.1. 确定可能适用的方法:编译器似乎应该知道,带有void块的lambda与Consumer方法匹配,带有返回类型的lambda与Function方法匹配。

我把以下未能编译的示例代码放在一起:

import java.util.function.Consumer;
import java.util.function.Function;

public class AmbiguityBug {
  public static void main(String[] args) {
    doStuff(getPattern(x -> System.out.println(x)));
    doStuff(getPattern(x -> String.valueOf(x)));
  }

  static Pattern<String, String> getPattern(Function<String, String> function) {
    return new Pattern<>(function);
  }

  static ConsumablePattern<String> getPattern(Consumer<String> consumer) {
    return new ConsumablePattern<>(consumer);
  }

  static void doStuff(Pattern<String, String> pattern) {
    String result = pattern.apply("Hello World");
    System.out.println(result);
  }

  static void doStuff(ConsumablePattern<String> consumablePattern) {
    consumablePattern.consume("Hello World");
  }

  public static class Pattern<T, R> {
    private final Function<T, R> function;

    public Pattern(Function<T, R> function) {
      this.function = function;
    }

    public R apply(T value) {
      return function.apply(value);
    }
  }

  public static class ConsumablePattern<T> {
    private final Consumer<T> consumer;

    public ConsumablePattern(Consumer<T> consumer) {
      this.consumer = consumer;
    }

    public void consume(T value) {
      consumer.accept(value);
    }
  }
}

我还发现了一个类似的stackoverflow帖子,结果是一个编译器错误。我的情况非常相似,尽管有点复杂。对我来说,这仍然看起来像一个错误,但我想确保我没有误解lambdas的语言规范。我使用Java8u45应该有所有最新的修复。

如果我将我的方法调用包装在一个块中,所有东西似乎都在编译,但这增加了额外的冗长性,许多自动格式化程序将把它重新格式化成多行。

doStuff(getPattern(x -> { System.out.println(x); }));
doStuff(getPattern(x -> { return String.valueOf(x); }));

共有1个答案

井嘉胜
2023-03-14

这句话肯定含糊不清:

doStuff(getPattern(x -> String.valueOf(x)));

请从链接的JLS章节中重新阅读:

如果满足以下所有条件,lambda表达式(§15.27)可能与函数接口类型(§9.8)兼容:

>

  • 目标类型的函数类型的属性与lambda表达式的属性相同。

    如果目标类型的函数类型具有void返回,则lambda主体是语句表达式(§14.8)或void兼容块(§15.27.2)。

    如果目标类型的函数类型具有(非void)返回类型,则lambda主体是表达式或值兼容块(§15.27.2)。

    消费者的情况下,您有一个语句表达式,因为任何方法调用都可以用作语句表达式,即使该方法是非无效的。例如,您可以简单地这样写:

    public void test(Object x) {
        String.valueOf(x);
    }
    

    它没有意义,但编译完美。你的方法可能有副作用,编译器不知道。例如,如果它是List.add,它总是返回true,没有人关心它的返回值。

    当然,这个lambda也符合函数,因为它是一个表达式。因此,这是一种含糊。如果有表达式而不是语句表达式,则调用将映射到函数,没有任何问题:

    doStuff(getPattern(x -> x == null ? "" : String.valueOf(x)));
    

    当您将其更改为{return String.valueOf(x);} ,则创建一个值兼容块,使其与函数匹配,但不符合空兼容块的条件。但是,块也可能有问题:

    doStuff(getPattern(x -> {throw new UnsupportedOperationException();}));
    

    这个块既符合值兼容又符合空格兼容,因此您又有了歧义。另一个环境块示例是一个无尽循环

    doStuff(getPattern(x -> {while(true) System.out.println(x);}));
    

    至于系统。出来println(x)case有点棘手。它肯定符合语句表达式的条件,因此可以与使用者匹配,但它似乎与表达式匹配,正如规范所说,方法调用是一个表达式。然而,它是一个使用有限的表达式,如15.12。他说:

    如果编译时声明无效,则方法调用必须是顶级表达式(即表达式语句中的表达式或for语句的ForInit或ForUpdate部分中的表达式),否则会发生编译时错误。这种方法调用不产生任何值,因此只能在不需要值的情况下使用。

    所以编译器完全遵循规范。首先,它确定lambda主体既是一个表达式(即使其返回类型为void:15.12.2.1在这种情况下也不例外)也是一个语句表达式,因此它也被认为是一个歧义。

    因此,对我来说,这两个语句都是根据规范编译的。ECJ编译器在此代码上生成相同的错误消息。

    一般来说,当您的重载具有相同数量的参数并且仅在可接受的函数接口中有差异时,我建议您避免重载您的方法。即使这些功能接口有不同的特性(例如,消费者Bi消费者):您对lambda没有问题,但对方法引用可能有问题。在这种情况下,只需为您的方法选择不同的名称(例如,ProcStuffconsumeStuff)。

  •  类似资料:
    • 给定以下变量 我想使用以下代码将占位符${name}替换为值“joe”(这不起作用) 但是,如果我按照“老式”的方式来做,一切都运行得很完美: )我一定漏了点什么

    • 问题内容: 给定以下变量 我想使用以下代码将占位符$ {name}替换为值“ Joe”(不起作用) 但是,如果我采用“旧式”方式,则一切都将正常运行: 我肯定在这里想念的东西:) 问题答案: 您还可以使用Stream.reduce(identity,accumulator,combiner)。 身份 是减少函数的初始值。 累加器 减少到,如果流是 顺序的 ,这是下一个减少的条件。 合路器 永远不要

    • 问题内容: 我有一个重载的方法,该方法分别接受一个Consumer和一个Function对象,并返回与相应的Consumer / Function匹配的泛型类型。我以为这会很好,但是当我尝试使用lambda表达式调用任一方法时,我收到一条错误消息,指示对该方法的引用不明确。 基于我对JLS§15.12.2.1的阅读。确定潜在的适用方法:似乎编译器应该知道我的带空块的lambda与Consumer方

    • 我需要从String的ArrayList中删除重复项,而不考虑大小写。对于情商: 上面的不会提供任何帮助,因为它工作在将返回false的equals方法上。有没有其他方法可以做以下事情:

    • 4.7. 使用 lambda 函数 4.7.1. 真实世界中的 lambda 函数 Python 支持一种有趣的语法,它允许你快速定义单行的最小函数。这些叫做 lambda 的函数,是从 Lisp 借用来的,可以用在任何需要函数的地方。 例 4.20. lambda 函数介绍 >>> def f(x): ... return x*2 ... >>> f(3) 6 >>> g =

    • 我需要制作两个lambda函数,一个用参数调用另一个,被调用的函数将参数打印出来。我很难让它工作:第一个函数: 请告知我应该在被调用函数中输入什么代码才能接收参数“hello Jenny”?非常感谢。