Java 8的变革:函数式编程和Lambda表达式探索
2025-06-24 12:15:44
来源:新华网
文章目录
- 一、函数接口
- 二、Lambda表达式简介
- 三、Lambda表达式外部参数
- 四、Lambda范例
- 五、Runnable Lambda表达式
一、函数接口
函数接口是一个具有单个抽象方法的接口,接口设计主要是为了支持 Lambda 表达式和方法引用,使得 Java 能更方便地实现函数式编程风格。
特点和用途:
- 单一抽象方法:函数接口只能有
一个
抽象方法,但可以有多个
默认方法(default)或静态方法(static)。 - Lambda 表达式:可以使用函数接口创建 Lambda 表达式,从而简洁地表示匿名函数,例如在集合操作、线程处理等场景中。
- 方法引用:可以通过函数接口的类型来引用一个已存在的方法,使代码更简洁和可读性更高。
Java 8 提供了几个标准的函数接口,接口通常位于
java.util.function
包中。
常见的函数接口:
Consumer:接收一个输入参数并且不返回结果的操作。
Consumer<String>printConsumer =str ->System.out.println(str);printConsumer.accept("Hello World!");
Supplier:不接收参数但是返回结果的提供型接口。
Supplier<Double>randomSupplier =()->Math.random();System.out.println(randomSupplier.get());
Function:接收一个输入参数,并返回结果。
Function<Integer,String>intToString =num ->String.valueOf(num);System.out.println(intToString.apply(123));
Predicate:接收一个输入参数,并返回一个布尔值结果。
Predicate<Integer>isEven =num ->num %2==0;System.out.println(isEven.test(5));// false
UnaryOperator:继承自 Function
,表示一元操作符。 UnaryOperator<Integer>square =num ->num *num;System.out.println(square.apply(5));// 25
自定义函数接口:只需确保接口中只有一个抽象方法即可。
@FunctionalInterfaceinterfaceMyFunctionalInterface{ voidmyMethod();// 允许有默认方法和静态方法defaultvoidanotherMethod(){ System.out.println("Default method");}}// 使用自定义的函数接口MyFunctionalInterfacemyFunc =()->System.out.println("Hello Custom Functional Interface");myFunc.myMethod();myFunc.anotherMethod();
二、Lambda表达式简介
Lambda 表达式可以被视为匿名函数的一种声明方式,允许将函数作为方法参数传递,或者在需要函数式接口的地方使用。
基本结构:
// parameters:参数列表,可以为空或非空// ->:箭头符号,分隔参数列表和Lambda表达式的主体// expression:单行表达式作为 Lambda 主体(parameters)->expression// { statements; }:代码块作为 Lambda 主体,可以包含多条语句和返回语句(parameters)->{ statements;}
表达式的特点:
- 简洁性和可读性:Lambda 表达式使代码更为简洁,尤其是在处理函数式接口时,省去了冗余的语法。
- 函数式编程风格:Lambda 表达式支持函数式编程,可以轻松地进行函数传递、方法引用和流式操作等。
- 闭包性:Lambda 表达式可以捕获其周围的变量,使得函数式编程中的状态管理更加灵活。
案例:通过 Lambda 表达式为
MathOperation
接口的operation
方法提供了四种不同的实现:加法、减法、乘法和除法。
- 接口的定义:
interfaceMathOperation{ intoperation(inta,intb);}
- 使用 Lambda 表达式来实现这个接口,以便传递不同的数学运算逻辑:
publicclassLambdaDemo{ publicstaticvoidmain(String[]args){ // Lambda 表达式实现加法MathOperationaddition =(inta,intb)->a +b;System.out.println("10 + 5 = "+operate(10,5,addition));// Lambda 表达式实现减法MathOperationsubtraction =(a,b)->a -b;System.out.println("10 - 5 = "+operate(10,5,subtraction));// Lambda 表达式实现乘法MathOperationmultiplication =(inta,intb)->{ returna *b;};System.out.println("10 * 5 = "+operate(10,5,multiplication));// Lambda 表达式实现除法MathOperationdivision =(a,b)->a /b;System.out.println("10 / 5 = "+operate(10,5,division));}privatestaticintoperate(inta,intb,MathOperationmathOperation){ returnmathOperation.operation(a,b);}}
三、Lambda表达式外部参数
Lambda 表达式有自己特定的作用域规则,可以捕获和访问其周围的变量, 可以随意引用外部变量,但如果外部变量是在当前作用域声明的,则一定不可以进行第二次赋值,哪怕是在 lambda 语句之后。
局部变量:Lambda 表达式可以访问它们所在方法的局部变量,但是这些变量必须是隐式最终或实际上是最终的(
final
)。这意味着变量一旦赋值后不再改变。Lambda 表达式内部不允许修改这些局部变量的值,否则编译器会报错。publicclassLambdaScopeDemo{ publicstaticvoidmain(String[]args){ intnum =10;// 局部变量MathOperationaddition =(inta,intb)->{ // num = 5; // 错误!Lambda 表达式不能修改局部变量的值// 这里访问了局部变量 numreturna +b +num;};System.out.println(addition.operation(5,3));}}
字段:Lambda 表达式可以访问外部类的字段(成员变量),包括实例字段和静态字段。
publicclassLambdaScopeDemo{ privatestaticintstaticNum;// 静态字段privateintinstanceNum;// 实例字段publicvoidtestLambdaScope(){ MathOperationaddition =(inta,intb)->{ // 访问实例字段和静态字段intresult =a +b +instanceNum +staticNum;returnresult;};System.out.println(addition.operation(5,3));}}
接口的默认方法:Lambda 表达式可以访问接口中定义的默认方法,但不能访问接口中定义的实例字段。
四、Lambda范例
使用Lambda表达式时,常见的场景包括对集合的遍历、排序、过滤以及与函数式接口的结合使用。
常见的Java 8 Lambda表达式示例:
- 遍历集合
publicstaticvoidmain(String[]args){ List<String>names =newArrayList<>();names.add("Alice");names.add("Bob");names.add("Charlie");// 使用 Lambda 表达式遍历集合names.forEach(name ->System.out.println(name));}
- 使用函数式接口进行计算
参考:二、Lambda表达式简介
- 使用函数式接口进行条件过滤
publicstaticvoidmain(String[]args){ List<Integer>numbers =Arrays.asList(1,2,3,4,5,6,7,8,9,10);// 使用 Lambda 表达式过滤偶数System.out.println("偶数:");filter(numbers,n ->n %2==0);// 使用 Lambda 表达式过滤大于 5 的数System.out.println("大于 5 的数:");filter(numbers,n ->n >5);}privatestaticvoidfilter(List<Integer>numbers,Predicate<Integer>condition){ for(Integernumber :numbers){ if(condition.test(number)){ System.out.print(number +" ");}}System.out.println();}
- 使用Comparator进行集合排序
publicstaticvoidmain(String[]args){ List<String>names =newArrayList<>();names.add("Alice");names.add("Bob");names.add("Charlie");// 使用 Lambda 表达式进行排序(根据字符串长度)names.sort((s1,s2)->s1.length()-s2.length());// 输出排序后的结果names.forEach(name ->System.out.println(name));}
- 使用 Runnable 执行代码块
参考:五、Runnable Lambda表达式
五、Runnable Lambda表达式
使用 Lambda 表达式来简洁地实现
Runnable
接口的实例化。Runnable
接口是一个函数接口,它只包含一个抽象方法void run()
,用于定义一个可以由线程执行的任务。
- 匿名内部类(Java 7 及之前):
Runnablerunnable =newRunnable(){ @Overridepublicvoidrun(){ System.out.println("Running in a separate thread");}};Threadthread =newThread(runnable);thread.start();
- Lambda 表达式(Java 8+):
Runnablerunnable =()->{ System.out.println("Running in a separate thread");};Threadthread =newThread(runnable);thread.start();
- 更简洁的方式:任务非常简单,可以进一步简化,直接将 Lambda 表达式作为参数传递给
Thread
的构造函数:
Threadthread =newThread(()->{ System.out.println("Running in a separate thread");});thread.start();
这种方式避免了显式地声明 Runnable
变量,使代码更加紧凑和易读。
案例:
publicstaticvoidmain(String[]args){ // 使用 Lambda 表达式创建一个新的线程Threadthread =newThread(()->{ for(inti =0;i <5;i++){ System.out.println("线程执行:"+i);try{ Thread.sleep(1000);}catch(InterruptedExceptione){ e.printStackTrace();}}});// 启动线程thread.start();}
莫道君行早,更有早行人