匿名内部类和 Lambda 表达式都可以用来实现一个函数接口(Functional Interface)的实例,本章将通过一个实例来介绍二者的区别。
我们先来看一个实例,启动一个线程向控制台输出一个字符串。
(1)匿名内部类实现方式:
package com.hxstrive.jdk8.lambda_theory; /** * 匿名实现方式 * @author hxstrive.com */ public class AnonymousDemo { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { System.out.println("匿名实现方式"); } }).start(); } }
(2)Lambda 实现方式:
package com.hxstrive.jdk8.lambda_theory; /** * Lambda实现方式 * @author hxstrive.com */ public class LambdaDemo { public static void main(String[] args) { new Thread(() -> { System.out.println("Lambda实现方式"); }).start(); } }
匿名内部类和 Lambda 表达式的主要区别如下:
从代码量上比较,Lambda 表达式的语法更加精简,代码量相对较小,而且可读性更强,降低了程序员的输入疲劳度。而匿名内部类语法冗长,代码量较大。
匿名内部类是 Java 的一种特性,语法形式比较冗长,需要使用关键字、大括号、分号等,使用起来稍显繁琐。而 Lambda 表达式是从 JDK8 开始引入的,语法简单,可以看作是对匿名内部类的一种简化和优化。
在使用 Lambda 表达式时,JDK8 可以推导出 Lambda 表达式的参数类型和返回类型。因此,使用 Lambda 表达式时不需要显示声明类型。而匿名内部类需要显式地声明接口和类类型。例如:
boolean flag = test((Integer val) -> { if(val > 100) { return true; } return false; }, 120); System.out.println("flag=" + flag);
匿名内部类中可以访问外部类的成员变量、方法参数和本地变量,它们的值都可以在匿名内部类中被修改。而 Lambda 表达式中只能捕获最终变量或者是事实上的最终变量(final 或者等价于 final),也就是只能读取外部变量,不能修改。如果需要修改则使用会报编译错误,如下:
java: 从lambda 表达式引用的本地变量必须是最终变量或实际上的最终变量
匿名接口实现可以有状态(成员变量),而 lambda 表达式则不能。请看这个接口:
public interface MyEventConsumer { public void consume(Object event); }
这个接口可以使用匿名接口实现,就像下面这样:
MyEventConsumer consumer = new MyEventConsumer() { public void consume(Object event){ System.out.println(event.toString() + " consumed"); } };
这个匿名的 MyEventConsumer 实现可以拥有自己的内部状态。看看这个重写的代码:
MyEventConsumer myEventConsumer = new MyEventConsumer() { // 内部状态 private int eventCount = 0; public void consume(Object event) { System.out.println(event.toString() + " consumed " + this.eventCount++ + " times."); } };
请注意,匿名 MyEventConsumer 的实现,现在有了一个名为 eventCount 的字段。lambda 表达式不能有这样的字段,因此 lambda 表达式可以说是无状态的。