【问题标题】:Java lambda expression - how interface name was omitted?Java lambda 表达式 - 如何省略接口名称?
【发布时间】:2019-08-28 16:47:45
【问题描述】:

我正在阅读关于 lambda 表达式的 oracle 教程:https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

要指定搜索条件,请实现 CheckPerson 界面:

interface CheckPerson {
    boolean test(Person p); }    

那就用吧

printPersons(
    roster,
    new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
        }
    }
);

然后

CheckPerson 接口是一个函数式接口。一个功能性的 接口是任何只包含一个抽象方法的接口。 (一种 功能接口可能包含一个或多个默认方法或静态 方法。)因为一个函数式接口只包含一个抽象 方法,您可以在实现时省略该方法的名称。到 这样做,而不是使用匿名类表达式,你使用 lambda 表达式,在下面的方法中突出显示 调用:

printPersons(
    roster,
    (Person p) -> p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25
);

他们说他们省略了方法,我在 lambda 中看不到 test - 这很清楚。但他们也删除了接口名称CheckPerson。为什么解释中没有提到?我们是否在 lambda 中使用CheckPerson 接口?

于 2019 年 8 月 29 日添加:
感谢 Alexey Soshin、Andrew Tobilko、Andreas(按回答时间顺序)的回答!我认为您的回答相互补充以提供完整的画面,因此我不能只选择一个作为接受。

【问题讨论】:

  • 检查printPersons方法的签名。它的第二个参数是CheckPerson 类型。无需指定,因此在 lamda 表达式中省略。

标签: java


【解决方案1】:

我们是否在 lambda 中使用 CheckPerson 接口?

我们不使用它,我们定义它。我们给它一个实现。我们通过编写 lambda 表达式来实现该接口。

“in lambda”是您的误解,因为 lambda 该接口的一个实例。

他们说他们省略了该方法,我在 lambda 中没有看到任何测试 - 这很清楚。但他们也删除了接口 CheckPerson 的名称。

这不是“丢弃”或“省略”某些东西。这是关于用另一种语法结构替换一种语法结构。您将匿名类替换为 lambda 表达式,以使您的代码更具表现力且不那么冗长。

【讨论】:

  • 谢谢,我错过了它的调用而不是声明。
  • Lambda 本身并不是严格意义上的实例,因为它不能引用自己的身份。您将无法从 lambda 主体调用其 default 方法,就像您使用匿名类实现相同的主体时一样。
  • @M. Prokhorov,“严格来说,Lambda 本身并不是一个实例,因为它不能引用自己的身份”——这很有趣。你能提供一个链接让我可以阅读更多详细信息吗?
  • @AlexeiMartianov 实际上,这是一个声明。调用将在方法 printPersons 中的某处
  • @AlexeiMartianov“以下调用”方法 printPersons,而不是您传入的 lambda
【解决方案2】:

在Java中称为SAM(单一抽象方法)类型,是的,仍然使用接口。

我们看printPersons的签名:

public static void printPersons(List<Person> roster, CheckPerson tester)

所以,Java 编译器知道,如果你为它提供一个 lambda,这个 lambda 应该符合 CheckPerson 提供的单一方法。

【讨论】:

    【解决方案3】:

    出于所有意图和目的,lambda 表达式是一种语法糖,用于简化您编写匿名类的方式。

    这并不完全正确,因为编译器实际上并没有生成匿名类(尽管它可以生成),并且诸如关键字 this 之类的东西改变了含义,但除此之外它确实非常相似。

    所以,使用匿名类的代码是:

    new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
        }
    }
    

    现在,由于编译器知道printPersons 的第二个参数必须是CheckPerson 对象,所以它可以在看到一个lambda 表达式时推断出这一点。由于接口只有一个方法,所以也可以推断出你需要实现这样一个方法。

    但是,您仍然必须列出参数,这意味着如果您删除可以从前两行推断的所有内容:

    new CheckPerson() {
        public boolean test(Person p) {
    

    你得到:

    (Person p) -> {
    

    -&gt; 是告诉编译器推断其余部分的 lambda 表达式语法。

    到目前为止,我们已经减少了:

    new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
        }
    }
    

    到:

    (Person p) -> {
        return p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25;
    }
    

    现在我们应用更多的语法糖,即让编译器做更多的工作,以删除“模板”的东西。

    • 参数类型可以推断,无需给出。

    • 如果只有一个参数,则不需要()括号。

    • 1234563 p>

    结果:

    p -> p.getGender() == Person.Sex.MALE
      && p.getAge() >= 18
      && p.getAge() <= 25
    

    这是与原始匿名类相同的简写 lambda 表达式(语法糖)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多