132. Java 泛型 - Lambda 表达式中的目标类型

75 阅读3分钟

132. Java 泛型 - Lambda 表达式中的目标类型

Java 中,Lambda 表达式(p -> 条件表达式) 本质上是对函数式接口的实现,但 Java 需要知道 Lambda 表达式属于哪个具体类型(即目标类型)。

在许多情况下,目标类型(Target Type 由**Lambda 所处的上下文**决定,编译器会根据它的用途推断出 Lambda 表达式的正确类型。


1. Lambda 表达式如何确定目标类型?

Java 编译器会自动推断 Lambda 表达式的类型,但前提是 Lambda 表达式所在的上下文必须提供明确的目标类型

✅ 示例 1:Lambda 作为方法参数

假设我们有以下两个方法:

import java.util.List;
import java.util.function.Predicate;

class Person {
    enum Sex { MALE, FEMALE }
    private String name;
    private int age;
    private Sex gender;

    // 构造方法
    public Person(String name, int age, Sex gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    // Getter 方法
    public int getAge() { return age; }
    public Sex getGender() { return gender; }
    public String getName() { return name; }
}

// 自定义接口
interface CheckPerson {
    boolean test(Person p);
}

public class LambdaTargetTypeDemo {
    // 使用自定义接口
    public static void printPersons(List<Person> people, CheckPerson tester) {
        for (Person p : people) {
            if (tester.test(p)) {
                System.out.println(p.getName());
            }
        }
    }

    // 使用 Java 内置的 Predicate<T>
    public static void printPersonsWithPredicate(List<Person> people, Predicate<Person> tester) {
        for (Person p : people) {
            if (tester.test(p)) {
                System.out.println(p.getName());
            }
        }
    }
}

然后,我们可以这样调用这两个方法:

import java.util.Arrays;
import java.util.List;

public class LambdaTargetTypeTest {
    public static void main(String[] args) {
        List<Person> people = Arrays.asList(
            new Person("Alice", 22, Person.Sex.FEMALE),
            new Person("Bob", 20, Person.Sex.MALE),
            new Person("Charlie", 30, Person.Sex.MALE)
        );

        // 目标类型:CheckPerson
        LambdaTargetTypeDemo.printPersons(
            people, 
            p -> p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25
        );

        // 目标类型:Predicate<Person>
        LambdaTargetTypeDemo.printPersonsWithPredicate(
            people, 
            p -> p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25
        );
    }
}

🔍 解析

  • printPersons() 方法需要一个 CheckPerson 类型的参数,因此 p -> ... 被推断为 CheckPerson 类型的实现。
  • printPersonsWithPredicate() 方法需要一个 Predicate<Person>,因此 p -> ... 被推断为 Predicate<Person> 类型的实现。

2. Lambda 表达式适用于哪些目标类型?

Lambda 表达式可以用于以下场景,前提是编译器能明确推断出目标类型

场景示例
变量声明CheckPerson checker = p -> p.getAge() > 18;
赋值(Assignment)Predicate<Person> predicate = p -> p.getAge() > 18;
方法返回值return p -> p.getAge() > 18;
数组初始化Predicate<Person>[] predicates = { p -> p.getAge() > 18 };
方法或构造函数参数printPersons(people, p -> p.getAge() > 18);
Lambda 表达式体Function<String, Integer> func = s -> s.length();
条件表达式(condition ? p -> p.getAge() > 18 : p -> p.getAge() < 18);
强制类型转换(Predicate<Person>) (p -> p.getAge() > 18);

3. Lambda 表达式的目标类型示例

✅ 示例 2:Lambda 作为变量

// 目标类型:CheckPerson
CheckPerson checker = p -> p.getAge() > 18;

// 目标类型:Predicate<Person>
Predicate<Person> predicate = p -> p.getAge() > 18;

🔍 解析

  • checker 变量的目标类型是 CheckPerson,因此 Lambda 表达式属于 CheckPerson
  • predicate 变量的目标类型是 Predicate<Person>,因此 Lambda 表达式属于 Predicate<Person>

✅ 示例 3:Lambda 作为返回值

public static Predicate<Person> getAdultFilter() {
    return p -> p.getAge() >= 18;
}

🔍 解析

  • getAdultFilter() 方法的返回类型是 Predicate<Person>,因此 Lambda 表达式 p -> p.getAge() >= 18 被推断为 Predicate<Person>

✅ 示例 4:Lambda 在数组初始化中

Predicate<Person>[] filters = { 
    p -> p.getAge() > 18, 
    p -> p.getGender() == Person.Sex.MALE 
};

🔍 解析

  • 数组 filters 的元素类型是 Predicate<Person>,因此 Lambda 表达式 p -> p.getAge() > 18p -> p.getGender() == Person.Sex.MALE 都被推断为 Predicate<Person>

✅ 示例 5:Lambda 作为条件表达式

boolean useAgeFilter = true;
Predicate<Person> filter = useAgeFilter 
    ? p -> p.getAge() > 18 
    : p -> p.getGender() == Person.Sex.MALE;

🔍 解析

  • Lambda 表达式 p -> p.getAge() > 18p -> p.getGender() == Person.Sex.MALE 必须具有相同的目标类型,即 Predicate<Person>

4. 目标类型的局限性

虽然目标类型可以让 Java 编译器自动推断 Lambda 表达式的类型,但有些情况下仍然需要显式声明类型。例如:

❌ 示例 6:编译器无法推断类型

Object obj = p -> p.getAge() > 18; // ❌ 编译错误

🔍 解析

  • Object 不是一个函数式接口,因此 Lambda 表达式没有可用的目标类型,导致编译错误。

5. 结论

目标类型(Target Type) 允许 Java 编译器根据 Lambda 表达式的上下文自动推断类型,避免冗余的类型声明。 ✅ Lambda 适用于方法参数、变量赋值、返回值、数组初始化等,前提是上下文能提供明确的目标类型。 ✅ 目标类型让 Java 泛型和 Lambda 结合得更紧密,提高了代码的简洁性和可读性!🚀