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() > 18和p -> 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() > 18和p -> 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 结合得更紧密,提高了代码的简洁性和可读性!🚀