阐明Lambda表达式在官方Java教程中的工作原理

时间:2014-11-16 00:10:18

标签: java

我一遍又一遍地阅读tutorial,我只是不明白方法6的最后部分。

这个部分:

public static void printPersonsWithPredicate(
    List<Person> roster, Predicate<Person> tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

因此,以下方法调用与在方法3中调用printPersons时相同:在本地类中指定搜索条件代码以获取有资格获得选择性服务的成员:

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

因此,如果 p 是泛型类型,它怎么知道它是一个人?我只是不知道如何将自己区分为一个人,然后调用Person方法 - getGender()和getAge()。在这种情况下,p是否引用了周围类的类对象?


更新

根据提供的答案,这是正确的吗?

以下代码:

interface CheckPerson {
    boolean test(Person p);
}

class CheckPersonEligibleForSelectiveService implements CheckPerson {
    public boolean test(Person p) {
        return p.gender == Person.Sex.MALE &&
            p.getAge() >= 18 &&
            p.getAge() <= 25;
    }
}

public static void printPersons(
    List<Person> roster, CheckPerson tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

printPersons(roster, new CheckPersonEligibleForSelectiveService());

与:

相同
import java.util.function // import the Predicate<T> interface

public static void printPersonsWithPredicate(
    List<Person> roster, Predicate<Person> tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

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

因为:

  1. 谓词接口已在java.util.function
  2. 中定义
  3. 通过在方法参数中声明Predicate为Person:
    1. 我们隐式使用Person类型实现Predicate,并且
    2. 在使用泛型类型T(或缺少类型)调用方法时,该方法隐式推断类型为Person
  4. 如果我错了,请告诉我,以便我能更新我的理解。我真的很想做到这一点。

3 个答案:

答案 0 :(得分:1)

lambda表达式是Predicate<Person>#test正文的定义,它将Person作为参数。我们知道这是因为printPersonsWithPredicate被声明为Predicate<Person>,lambda作为参数被传递。

lambda的语法是->的左侧是正在定义的方法的参数列表:

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

但是,编译器能够确定lambda的奇异参数p必须是Person,因此可以省略它。因此缩短形式p -> ...

Approach #4中显示了与lambda相近的匿名类。

答案 1 :(得分:1)

一般情况下:参数只是从相关功能界面的类型签名推断,包括对通用参数的任何其他限制

在这种情况下,我们知道第二个参数是Predicate<T>。由于Predicate<T>是方法test(T t)的功能界面,因此我们知道p的类型为T(并且不是Predicate<T> )。

由于T 的签名 Person仅限于printPersonsWithPredicate(List<Person> roster, Predicate<Person> tester),因此p的实际类型为Person


RE:更新 - 答案是&#34;是&#34;和&#34;不&#34; :)

&#34;是&#34;,一般来说,类型推断的顺序可以用这种方式描述。

&#34;否&#34;,原因如下:

  • 在2.2中,我说最好说明方法限制 TPerson 的类型。类型人只是&#34;共同点&#34;什么是允许的。例如:如果方法的签名包含:
    • Predicate<? extends JLabel> - 然后参数类型实际上是JLabel
    • Predicate<?> - 然后参数类型实际上是Object
  • 最好说明编译器推断有关类型的内容,而不是“#34;隐式定义&#34;除了编译器之外的其他东西。你可能会认为这是一个挑剔,但这个开关有两个重要的后果:
    • 它可以为你保存免受推理陷阱表格&#34;它不是X做N,它是Y&#34;。
    • 非常具体,因为编译器能够推断Predicate<Person>的类型是合法的,所以无需实际创建新的匿名类实现功能接口。实际上,在Java 8的当前JDK中,发生了什么Here's an article解释详情。

答案 2 :(得分:0)

仅解决更新问题...

  

1)谓词接口已在java.util.function

中定义

正确

  

2)通过在方法参数中声明Predicate为Person:

不正确的。

您实际上已将tester声明为Predicate<Person>。你在这里根本没有声明PredicatePredicate是标准库声明的标准接口。

  

2a)我们隐式使用Person类型

实现谓词

正确......有点儿。您明确实施它。此处所需的lambda类型由printPersonsWithPredicate的方法签名显式指定 ....具体来说,因为您声明形式参数tester具有类型Predicate<Person>

  

2b)当使用泛型类型T(或缺少T)调用该方法时,该方法隐式推断该类型为Person

不是这种情况。