当前位置: 首页 > news >正文

Java8:Lambda表达式

目录

1、出现原因

2、概念

3、使用

3.1 基本语法

3.2 函数式接口:Lambda的类型

3.3 方法引用

4、优点

5、限制

5.1 只能用于函数式接口

5.2 变量捕获限制

5.3 this关键字含义不同

5.4 调试困难

5.5 性能考虑

5.6 类型推断限制

6、应用场景

6.1 集合操作

6.2 事件处理

6.3 线程处理

6.4 条件执行


1、出现原因

背景与需求

  • 代码冗余问题:在Java 8之前,使用匿名内部类实现函数式接口时代码冗长

  • 函数式编程趋势:受到函数式语言(如Scala、Python)的影响

  • 并行处理需求:多核处理器时代需要更简洁的并发编程方式

  • API设计改进:让集合操作等API更加灵活和表达性强

示例对比

// Java 8 之前 - 使用匿名内部类
Collections.sort(list, new Comparator<String>() {@Overridepublic int compare(String s1, String s2) {return s1.compareTo(s2);}
});// Java 8 之后 - 使用Lambda表达式
Collections.sort(list, (s1, s2) -> s1.compareTo(s2));

2、概念

核心定义:Lambda 表达式是一个匿名函数

  • 匿名:它没有显式的方法名。

  • 函数:它像方法一样,有参数列表、一个函数体(执行代码)以及可能的返回值。

  • 可传递:Lambda 表达式可以作为参数传递给方法,或者赋值给一个变量。这是它最强大的地方。

主要功能:

  • 简化函数式接口的实现

  • 支持函数式编程风格

  • 提高代码的可读性和简洁性

  • 便于并行处理

  • 增强集合操作能力

3、使用

3.1 基本语法

(parameters) -> expression
// 或
(parameters) -> { statements; }
  • 参数列表 (parameters):

    • 类似于方法的参数列表,放在圆括号中。

    • 可以声明参数的类型,但编译器通常可以从上下文中推断出类型,因此大多数时候可以省略。

    • 如果只有一个参数,且类型可被推断,圆括号 () 也可以省略。

    • 如果没有参数,必须使用空括号 ()

  • 箭头操作符 ->:

    • 它将参数列表与 Lambda 主体分隔开。

  • Lambda 主体 { ... }:

    • 包含了要执行的代码。

    • 如果主体只有一条语句,花括号 {} 和 return 关键字(如果有返回值)都可以省略。

    • 如果主体包含多条语句,则必须使用花括号 {},并且如果需要返回值,必须显式使用 return 语句。

语法示例:

// 1. 无参数,返回值类型为 void
() -> System.out.println("Hello World")// 2. 一个参数,无括号 (Consumer),主体为一条语句
s -> System.out.println(s)// 3. 一个参数,有括号 (Function)
(s) -> s.length()// 4. 多个参数,类型推断 (Comparator)
(a, b) -> a.compareTo(b)// 5. 指定参数类型 (Comparator)
(String a, String b) -> a.compareTo(b)// 6. 多行代码,需要 return 和 {}
(int a, int b) -> {int sum = a + b;return sum;
}

3.2 函数式接口:Lambda的类型

Lambda 表达式不能独立存在,它必须被赋值给一个函数式接口类型的变量。可以把 Lambda 表达式看作是实现了一个函数式接口的、简洁的匿名内部类。

工作原理:当在代码中写下一个 Lambda 表达式时,编译器需要知道这个表达式的“目标类型”。这个目标类型必须是一个函数式接口。然后,Lambda 表达式就相当于实现了该接口唯一的抽象方法。

函数式接口的定义:一个只有一个抽象方法的接口。关键点:

  • 单一抽象方法(SAM):这是最重要的规则。它只能有一个抽象方法。

  • 默认方法和静态方法:函数式接口可以有任意数量的 default 方法或 static 方法。这些有实现的方法不计入“抽象方法”的数量。

  • @FunctionalInterface 注解:这个注解不是必须的,但强烈推荐使用。它的作用有:

    • 编译器检查:告诉编译器“请检查这个接口是否符合函数式接口的定义”。如果不符合(比如没有抽象方法或多个抽象方法),编译器会报错。

    • 文档化:让阅读代码的人一目了然地知道这是一个函数式接口,目的是为了配合 Lambda 表达式使用。

示例:

// 1. 传统的匿名内部类方式
Runnable runnable1 = new Runnable() {@Overridepublic void run() { // 实现唯一的抽象方法 ‘run'System.out.println("Running...");}
};// 2. Lambda 表达式方式
Runnable runnable2 = () -> System.out.println("Running...");

在上面的例子中:

  • Runnable 是一个函数式接口(它只有一个 run() 方法)。

  • Lambda 表达式 () -> System.out.println("Running...") 就是 run() 方法的实现。

  • 变量 runnable2 的类型是 Runnable,这就是 Lambda 表达式的类型。

Java 8 在 java.util.function 包中为我们预定义了大量常用的函数式接口,覆盖了大部分常见场景:

接口参数返回值描述抽象方法常见用途
Consumer<T>Tvoid消费型接口void accept(T t)遍历集合,打印 list.forEach(s -> System.out.println(s))
Supplier<T>T供给型接口T get()工厂方法 () -> new ArrayList<>()
Function<T, R>TR函数型接口R apply(T t)

类型转换 s -> s.length()

Predicate<T>Tboolean断言型接口boolean test(T t)过滤集合 s -> s.startsWith("A")
UnaryOperator<T>TT一元操作符T apply(T t)s -> s.toUpperCase()
BinaryOperator<T>(T, T)T二元操作符T apply(T t1, T t2)(a, b) -> a + b
// Function<T, R>:R apply(T t)
// 数据转换,如将字符串转为整数。
Function<String, Integer> stringToInteger = s -> Integer.parseInt(s);
Integer number = stringToInteger.apply("123"); // 返回 123// Predicate<T>:boolean test(T t)
// 过滤集合中的元素
Predicate<String> isEmpty = s -> s == null || s.trim().isEmpty();
boolean result = isEmpty.test(" "); // 返回 true
List<String> list = Arrays.asList("A", "BB", "CCC");
List<String> filtered = list.stream().filter(s -> s.length() > 1).toList(); // [BB, CCC]// Consumer<T>:void accept(T t)
// 遍历集合,打印元素
Consumer<String> printer = s -> System.out.println("Element: " + s);
printer.accept("Hello"); // 打印 "Element: Hello"
list.forEach(printer); // 遍历打印整个 list// Supplier<T>:T get()
// 惰性求值,对象工厂
Supplier<Double> randomSupplier = () -> Math.random();
Double randomValue = randomSupplier.get(); // 获取一个随机数// UnaryOperator<T>: 继承自 Function<T, T>,输入和输出类型相同。 T apply(T t)
UnaryOperator<String> toUpper = s -> s.toUpperCase();// BinaryOperator<T>: 接受两个同类型的参数,返回一个同类型的结果。 T apply(T t1, T t2)
BinaryOperator<Integer> add = (a, b) -> a + b;// BiFunction<T, U, R>: 接受两个参数 (T, U),返回结果 R。 R apply(T t, U u)// BiConsumer<T, U>: 接受两个参数 (T, U),不返回结果。 void accept(T t, U u)

自定义函数式接口示例

@FunctionalInterface
interface MyCalculator {int calculate(int a, int b); // 唯一的抽象方法
}public class Test {public static void main(String[] args) {// 使用 Lambda 表达式实现这个接口MyCalculator adder = (x, y) -> x + y;MyCalculator multiplier = (x, y) -> x * y;System.out.println(adder.calculate(5, 3)); // 输出 8System.out.println(multiplier.calculate(5, 3)); // 输出 15}
}

3.3 方法引用

方法引用是 Lambda 表达式的一种更简洁的写法,当 Lambda 体仅仅是调用一个已存在的方法时使用。

语法目标对象或类 :: 方法名

// 静态方法引用
List<Integer> numbers = Arrays.asList(1, 2, 3);
numbers.forEach(System.out::println);// 实例方法引用
List<String> strings = Arrays.asList("a", "b", "c");
strings.forEach(String::toUpperCase);// 构造方法引用
Supplier<List<String>> listSupplier = ArrayList::new;

4、优点

  • 代码简洁性
  • 函数式编程支持
  • 并行处理能力
  • 提高可读性
  • 延迟执行
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);// 函数式操作
int sum = numbers.stream().filter(n -> n % 2 == 0).map(n -> n * 2).reduce(0, Integer::sum);List<String> list = Arrays.asList("a", "b", "c");// 串行处理
list.stream().forEach(System.out::println);
// 并行处理
list.parallelStream().forEach(System.out::println);// 业务逻辑更清晰
List<Person> adults = people.stream().filter(person -> person.getAge() >= 18).sorted((p1, p2) -> p1.getName().compareTo(p2.getName())).collect(Collectors.toList());// 延迟执行
Supplier<String> lazyMessage = () -> {System.out.println("Computing message...");return "Hello World";
};
// 只有在get()调用时才会执行
String result = lazyMessage.get();

5、限制

5.1 只能用于函数式接口

// 正确 - 函数式接口(只有一个抽象方法)
@FunctionalInterface
interface MyFunction {int operate(int a, int b);
}MyFunction add = (a, b) -> a + b;// 错误 - 多个抽象方法的接口不能使用Lambda
interface InvalidInterface {void method1();void method2();  // 编译错误
}

5.2 变量捕获限制

void outerMethod() {int count = 0;  // 必须是final或等效final// 错误:在Lambda中修改外部变量// Runnable r = () -> count++;  // 编译错误// 正确:只能读取final或等效final变量Runnable r = () -> System.out.println(count);
}

访问局部变量:Lambda 可以捕获外部方法的局部变量,但这些变量必须是 final 或等效 final (effectively final)。等效 final 意味着变量在初始化后值没有再被修改过。

  • 原因:局部变量存在于栈上,而 Lambda 可能在另一个线程中执行,其生命周期可能比创建它的方法更长。为了保证数据一致性,Java 采用了值捕获,即复制变量的值。如果变量可变,就会导致数据不同步。

访问实例变量和静态变量:Lambda 可以自由地读取和修改外部类的实例变量和静态变量,因为它们存储在堆中,是共享的。

5.3 this关键字含义不同

this 关键字:在 Lambda 表达式内部,this 指的是创建这个 Lambda 表达式的外部类实例,而不是 Lambda 表达式本身。这与匿名内部类不同(匿名内部类的 this 指向自身)。

public class ThisDemo {private String value = "Outer class value";public void testLambda() {Runnable r = () -> {// 这里的this指向包含Lambda的外部类实例System.out.println("this.value: " + this.value);System.out.println("this.getClass(): " + this.getClass());// 不能在Lambda内部定义与外部变量同名的变量// String value = "Lambda value"; // 编译错误};r.run();}public static void main(String[] args) {new ThisDemo().testLambda();}
}

输出结果:

this.value: Outer class value
this.getClass(): class ThisDemo

5.4 调试困难

// Lambda表达式在调试时可能显示为匿名方法
List<Integer> numbers = Arrays.asList(1, 2, 3);
numbers.stream().map(n -> n * 2)          // 调试时可能显示为lambda$0.forEach(System.out::println);

5.5 性能考虑

// 在性能关键场景需要谨慎使用
public void performanceTest() {// 频繁创建的Lambda可能影响性能for (int i = 0; i < 1000000; i++) {Runnable r = () -> System.out.println(i);// 每次循环都会创建新的Lambda实例}
}

5.6 类型推断限制

// 在某些复杂情况下需要显式类型声明
// 编译错误:类型推断失败
// BinaryOperator add = (x, y) -> x + y;// 正确:显式声明类型
BinaryOperator<Integer> add = (Integer x, Integer y) -> x + y;

6、应用场景

6.1 集合操作

List<User> users = getUserList();// 过滤、转换、收集
List<String> names = users.stream().filter(user -> user.getAge() > 18).map(User::getName).sorted().collect(Collectors.toList());

6.2 事件处理

// Swing事件处理
JButton button = new JButton("Click");
button.addActionListener(e -> {System.out.println("Button clicked");performAction();
});

6.3 线程处理

// 创建线程
new Thread(() -> {// 后台任务processData();updateUI();
}).start();

6.4 条件执行

// 延迟执行和条件检查
public void processIfValid(Supplier<Boolean> validator, Runnable processor) {if (validator.get()) {processor.run();}
}// 使用
processIfValid(() -> checkCondition(), () -> doWork());

http://www.dtcms.com/a/542156.html

相关文章:

  • 医院网站建设 价格石家庄网站优化招聘
  • 【AIGC】2025:MV-Crafter: An Intelligent System for Music-guided Video Generation
  • 电脑版网站建设合同营销网站开发
  • 下载office home and student 2019版本
  • 东莞南城网站建设公司怎么样免费流程图制作网站
  • 锦州做网站公司判断网站模板版本
  • 【测试理论和实践 3.BUG篇】
  • 媒体网站建设包头移动官网网站建设
  • 金华网站建设报价系统优化助手
  • Ninja Ripper游戏模型贴图提取
  • 51c大模型~合集43
  • 做网站开发要学什么网站营销的定义
  • 【GESP/CSP】编程武器库-3, 十六进制转换十进制
  • 客服AI软件正在改变新人培训的方式
  • Vue 3 + Element Plus 表格操作封装:useTableOperations 组合式函数详解
  • 可信网站值得做吗企业网站建设哪家公司好
  • 上海中小企业网站深圳网络推广引流
  • 网站备案管局电话福建省建设职业管理中心网站
  • 健康管理虚拟仿真实训室:模拟诊疗,实战演练
  • 个人网站备案要钱吗最容易做流量的网站
  • html+jsp个人网站模板全国分类信息网站排名
  • 2025年GDPR合规全景:原则、实践与行业案例
  • Winform控件:Combobox
  • 义乌水务建设集团官方网站佛山市网站建设分站多少钱
  • iOS 应用加固软件选型与落地 多工具组合构建可审计的 IPA 混淆与加固流水线
  • 做网站手机端不做PC可以吗怎么做弹幕小视频网站
  • 网站移动端怎么做的免费的高清视频素材网站
  • 类变量和类方法main方法代码块单例设计模式final关键字
  • 网站的打开速度汽车网站建设报价
  • 建设asp网站视频教程锡林郭勒盟工程建设造管理网站