目录
一、简介
二、组成部分
三、使用场景
四、优点与注意事项
五、函数式接口
六、方法引用
七、使用示例
一、简介
Lambda表达式是 Java 8 引入的新特性,提供了一种简洁明了的方式来表示匿名函数(没有声明的方法)。它使得函数式编程在Java中成为可能,大大简化了代码,提高了可读性和可维护性。
基本语法:
(参数列表) -> 表达式
或
(参数列表) -> { 代码块 }
- 参数列表:可以为空(
()
)或包含一个或多个参数。 - 箭头操作符:
->
,将参数列表与方法体分隔开。 - 方法体:可以是一个表达式或一段代码块。
二、组成部分
Lambda表达式由以下三个部分组成:
-
参数列表:
- 表示输入参数,可以有0个、1个或多个。
- 如果有多个参数,用逗号分隔。
- 类型声明可选:参数类型可以明确声明,也可以省略,由编译器根据上下文推断。
- 如果只有一个参数,圆括号可以省略。
示例:
(int x, int y) -> x + y // 类型声明 (x, y) -> x + y // 类型推断 x -> x * x // 单个参数,省略括号 () -> System.out.println("Hello") // 无参数
-
箭头操作符 (
->
):- 将参数列表与方法体分隔开。
->
是Lambda表达式的核心语法符号。
-
方法体:
- 定义Lambda表达式要执行的功能,可以是:
- 单个表达式(无需大括号,值会自动作为返回值)。
- 代码块(用大括号包裹,需要显式使用
return
返回值)。
- 方法体的形式决定大括号和返回关键字的使用规则:
- 单表达式:省略大括号和
return
。 - 代码块:必须加大括号,返回值需显式
return
。
- 单表达式:省略大括号和
示例:
(x, y) -> x + y // 单表达式,返回x + y的值 (x, y) -> { return x + y; } // 代码块,显式返回x + y的值 () -> System.out.println("Hello World") // 无返回值
- 定义Lambda表达式要执行的功能,可以是:
三、使用场景
1. 函数式接口
函数式接口是只包含一个抽象方法的接口,通常会在接口上使用@FunctionalInterface
注解。Lambda表达式最常见的用途就是替代匿名内部类来实现函数式接口。
示例:
@FunctionalInterface
interface MathOperation {
int operation(int a, int b);
}
// 使用Lambda表达式
MathOperation addition = (a, b) -> a + b;
int result = addition.operation(5, 3); // 输出8
2. 结合集合框架
Lambda表达式与Java集合框架的Stream API
紧密结合,方便进行集合数据的过滤、映射、归约等操作。
示例:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// 过滤出以字母'C'开头的名字并打印
names.stream()
.filter(name -> name.startsWith("C"))
.forEach(System.out::println); // 输出 "Charlie"
四、优点与注意事项
优点:
- 代码简洁:消除了匿名内部类的冗长代码,使代码更加精炼。
- 可读性高:代码结构清晰,逻辑一目了然。
- 提高效率:支持并行流处理,充分利用多核CPU的优势。
- 灵活性强:与函数式接口、方法引用等特性结合,增强了代码的灵活性。
注意事项:
- 只能用于函数式接口:Lambda表达式需要一个目标类型,该类型必须是函数式接口。
- 变量作用域:Lambda表达式中使用的局部变量必须是
final
或有效的final(即其值在初始化后未被修改)。 - 检查异常:Lambda表达式中如果抛出受检异常,必须在接口的抽象方法中声明该异常。
- 调试困难:Lambda表达式的堆栈信息可能不如传统代码直观,调试时需要注意。
五、函数式接口
Java 8在java.util.function
包中提供了一系列通用的函数式接口,如:
-
Predicate<T>
:接收一个参数,返回boolean
值。Predicate<String> isEmpty = s -> s.isEmpty(); System.out.println(isEmpty.test("")); // true System.out.println(isEmpty.test("Hello")); // false
-
Function<T, R>
:接收一个参数,返回一个结果。Function<Integer, String> intToString = num -> "Number: " + num; System.out.println(intToString.apply(10)); // 输出 "Number: 10"
-
Consumer<T>
:接收一个参数,无返回值。Consumer<String> printer = s -> System.out.println(s); printer.accept("Hello World!"); // 输出 "Hello World!"
-
Supplier<T>
:不接收参数,返回一个结果。Supplier<Double> randomSupplier = () -> Math.random(); System.out.println(randomSupplier.get()); // 输出一个随机数
-
BinaryOperator<T>
:接收两个相同类型的参数,返回一个相同类型的结果。BinaryOperator<Integer> adder = (a, b) -> a + b; System.out.println(adder.apply(10, 20)); // 输出 30
六、方法引用
如果Lambda表达式仅调用一个已存在的方法,可以使用方法引用来替代,语法更加简洁。
语法:
- 对象的实例方法引用:
instance::method
- 类的静态方法引用:
Class::staticMethod
- 类的实例方法引用:
Class::instanceMethod
示例:
public class LambdaClassSuper {
LambdaInterface sf(){
return null;
}
}
public class LambdaClass extends LambdaClassSuper {
public static LambdaInterface staticF() {
return null;
}
public LambdaInterface f() {
return null;
}
void show() {
//1.调用静态函数,返回类型必须是functional-interface
LambdaInterface t = LambdaClass::staticF;
//2.实例方法调用
LambdaClass lambdaClass = new LambdaClass();
LambdaInterface lambdaInterface = lambdaClass::f;
//3.超类上的方法调用
LambdaInterface superf = super::sf;
//4. 构造方法调用
LambdaInterface tt = LambdaClassSuper::new;
}
}
七、使用示例
实现线程:
// 传统方式
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread is running");
}
}).start();
// 使用Lambda表达式
new Thread(() -> System.out.println("Thread is running")).start();
列表排序:
List<Integer> strings = Arrays.asList(1, 2, 3);
//传统方式
Collections.sort(strings, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;}
});
//Lambda
Collections.sort(strings, (Integer o1, Integer o2) -> o1 - o2);
//分解开
Comparator<Integer> comparator = (Integer o1, Integer o2) -> o1 - o2;
Collections.sort(strings, comparator);