一、Lambda表达式
1.1 Lambda表达式介绍
Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。
在Java语言中,可以为变量赋予一个值:
能否把一个代码块赋给一变量吗?
在Java 8之前,这个是做不到的。但是Java 8问世之后,利用Lambda特性,就可以做到了。
甚至我们可以让语法变得更简洁。
在Java 8里面,所有的Lambda的类型都是一个接口,而Lambda表达式本身,也就是”那段代码“,需要是这个接口的实现。这是我认为理解Lambda的一个关键所在,简而言之就是,Lambda表达式本身就是一个接口的实现。直接这样说可能还是有点让人困扰,我们继续看看例子。我们给上面的aBlockOfCode加上一个类型:
这种只有一个接口函数需要被实现的接口类型,我们叫它”函数式接口“。只要”函数式接口“才能使用Lambda表达式。为了避免后来的人在这个接口中增加接口函数导致其有多个接口函数需要被实现,变成"非函数接口”,我们可以在这个上面加上一个声明@FunctionalInterface, 这样别人就无法在里面添加新的接口函数了。
1.2 Lambda作用
最直观的作用就是使得代码变得异常简洁。
接口要求
虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。Lambda 规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法。
jdk 8 中有另一个新特性:default, 被 default 修饰的方法会有默认实现,不是必须被实现的方法,所以不影响 Lambda 表达式的使用。也就是说一个接口中有且仅有一个抽象方法,还有default实现的方法也能使用Lambda表达式。
@FunctionalInterface注解作用:
@FunctionalInterface标记在接口上,“函数式接口”是指仅仅只包含一个抽象方法的接口。
1.3 Lambda表达式语法
1.3.1 Lambda表达式结构
语法结构:
(parameters) -> expression
或
(parameters) ->{ statements;}
语法形式为 () -> {}:
() 用来描述参数列表,如果有多个参数,参数之间用逗号隔开,如果没有参数,留空即可;
-> 读作(goes to),为 lambda运算符 ,固定写法,代表指向动作;
{} 代码块,具体要做的事情,也就是方法体内容;
1.3.2 Lambda表达式的重要特征
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
可选的参数圆括号”()“:一个参数无需定义圆括号,但多个参数需要定义圆括号。
可选的大括号”{ }“:如果主体包含了一个语句,就不需要使用大括号。
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
// 1. 不需要参数,返回值为 5 () -> 5 // 2. 接收一个参数(数字类型),返回其2倍的值 x -> 2 * x // 3. 接受2个参数(数字),并返回他们的差值 (x, y) -> x – y // 4. 接收2个int型整数,返回他们的和 (int x, int y) -> x + y // 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void) (String s) -> System.out.print(s)
1.3 Lambda表达式使用
1.3.1 定义函数接口并使用Lambda表达式实现函数接口
函数接口指的是在该接口中只能含有一个抽象方法。可以有多个default修饰的方法或者是static方法。
package cn.it.bz.Lambda; //无返回值无参数的函数接口 @FunctionalInterface interface NoReturnNoParam{ public void method(); } //无返回值有一个参数的函数接口 @FunctionalInterface interface NoReturnOneParam { public void method(int a); } //无返回值有两个参数的函数接口 @FunctionalInterface interface NoReturnManyParam { public void method(int a, int b); } //有返回值的无参数函数接口 @FunctionalInterface interface ReturnNoParam { public int method(); } //有返回值有一个参数的函数接口 @FunctionalInterface interface ReturnOneParam { public int method(int a); } //有返回值有两个参数的函数接口 @FunctionalInterface interface ReturnManyParam { public int method(int a, int b); } public class Test { public static void main(String[] args) { //无返回值无参数的函数接口实现,类型就是接口名称,{}表示对抽象方法的具体实现 NoReturnNoParam noReturnNoParam = () -> { System.out.println("noReturnNoParam"); }; //调用该方法 noReturnNoParam.method(); System.out.println("----------------------"); //无返回值一个参数的接口实现 NoReturnOneParam noReturnOneParam = (int a) -> { System.out.println("noReturnOneParam"+a); }; //调用该方法 noReturnOneParam.method(1); System.out.println("---------------------"); //无返回值的两个参数的接口实现 NoReturnManyParam noReturnManyParam = (int a, int b) -> { System.out.println("noReturnManyParam"+a+","+b); }; //调用该方法 noReturnManyParam.method(1, 2); System.out.println("--------------------"); //有返回值无参数的函数接口实现 ReturnNoParam returnNoParam = () -> { System.out.println("returnNoParam"); return 123; }; //调用该方法 int a = returnNoParam.method(); System.out.println("a="+a); System.out.println("---------------------"); //有返回值有一个参数的函数接口实现 ReturnOneParam returnOneParam = (int a1) -> { System.out.println("returnOneParam"+a1); return a1; }; //调用该方法 int b = returnOneParam.method(1); System.out.println("b="+b); System.out.println("--------------------"); //有返回值有两个参数的函数接口实现 ReturnManyParam returnManyParam = (int a1, int a2) -> a1 + a2; // 调用该方法 int c = returnManyParam.method(1, 2); System.out.println("c="+c); } }
1.3.2 简化Lambda表达式
1、只有一个参数时小括号可以省略。
2、参数列表中的参数可以写,可以不写。要写都写,
3、当方法体之哟一行代码时,大括号可以省略。
4、方法体中只有一行return时,return可以省略。
package cn.it.bz.Lambda; //无返回值无参数的函数接口 @FunctionalInterface interface NoReturnNoParam{ public void method(); } //无返回值有一个参数的函数接口 @FunctionalInterface interface NoReturnOneParam { public void method(int a); } //无返回值有两个参数的函数接口 @FunctionalInterface interface NoReturnManyParam { public void method(int a, int b); } //有返回值的无参数函数接口 @FunctionalInterface interface ReturnNoParam { public int method(); } //有返回值有一个参数的函数接口 @FunctionalInterface interface ReturnOneParam { public int method(int a); } //有返回值有两个参数的函数接口 @FunctionalInterface interface ReturnManyParam { public int method(int a, int b); } public class Test { public static void main(String[] args) { //无返回值无参数的函数接口实现 //方法体只有一行代码时,{}可以不写。 NoReturnNoParam noReturnNoParam = () -> System.out.println("noReturnNoParam");; //调用该方法 noReturnNoParam.method(); System.out.println("----------------------"); //无返回值一个参数的接口实现 //当参数只有一个时,()可以不写;方法体只有一行代码时,{}可以不写。 NoReturnOneParam noReturnOneParam = a-> System.out.println("noReturnOneParam"+a);; //调用该方法 noReturnOneParam.method(1); System.out.println("---------------------"); //无返回值的两个参数的接口实现 //方法体只有一行代码时,{}可以不写。 NoReturnManyParam noReturnManyParam = (int a, int b) -> System.out.println("noReturnManyParam"+a+","+b);; //调用该方法 noReturnManyParam.method(1, 2); System.out.println("--------------------"); //有返回值无参数的函数接口实现 ReturnNoParam returnNoParam = () -> { System.out.println("returnNoParam"); return 123; }; //调用该方法 int a = returnNoParam.method(); System.out.println("a="+a); System.out.println("---------------------"); //有返回值有一个参数的函数接口实现 //当参数只有一个时,()可以不写; ReturnOneParam returnOneParam = (int a1) -> { System.out.println("returnOneParam"+a1); return a1; }; //调用该方法 int b = returnOneParam.method(1); System.out.println("b="+b); System.out.println("--------------------"); //当方法体只有return一行代码时,return可以不写。 ReturnManyParam returnManyParam = (int a1, int a2) -> a1 + a2; // 调用该方法 int c = returnManyParam.method(1, 2); System.out.println("c="+c); } }
1.3.3 Lambda表达式引用方法
有时候我们不是必须使用Lambda的函数体定义实现,我们可以利用 lambda表达式指向一个已经存在的方法作为抽象方法的实现。
要求
- 参数的个数以及类型需要与函数接口中的抽象方法一致。
- 返回值类型要与函数接口中的抽象方法的返回值类型一致。
语法
方法归属者::方法名 静态方法的归属者为类名,非静态方法归属者为该对象的引用。
package cn.it.bz.Lambda; @FunctionalInterface interface ReturnOne { public int method(int a); } public class Test2 { //静态方法 public static int doubleNumber(int a){ return a*2; } //非静态方法 public int doubleNumber2(int a) {return a * 2;} public static void main(String[] args) { //将静态方法作为接口中抽象方法的实现方法(前提是参数个数和参数类型必须相同) //Test2::doubleNumber表示抽象方法的实现方法是Test类下的doubleNumber方法 ReturnOne returnOne1 = Test2::doubleNumber; //调用 int method = returnOne1.method(10); System.out.println(method); //将非静态方法作为接口中抽象方法的实现方法 //先实例化非静态方法的归属者,通过对象引用实现 Test2 test2 = new Test2(); ReturnOne returnOne2 = test2::doubleNumber2; int method1 = returnOne1.method(20); System.out.println(method1); } }
1.3.4 创建线程
package cn.it.bz.Lambda; //Runnable接口中只有一个抽象方法run,也就是说Runnable是个函数接口。 public class Test3 { public static void main(String[] args) { System.out.println("主线程"+ Thread.currentThread().getName()+"启动!"); //Lambda表达式实现run 方法。 Runnable runnable = () -> { for (int i = 0; i < 10; i++ ) { System.out.println(Thread.currentThread().getName() + ", "+i); try { Thread.sleep(1000); } catch (InterruptedException e) { } } }; //线程包装 Thread thread = new Thread(runnable, "Lambda线程"); //线程启动 thread.start(); System.out.println("主线程"+ Thread.currentThread().getName()+"结束!"); } }
//或者直接将run方法的实现放在Thread构造方法中。 new Thread(() -> { for (int i = 0; i < 10; i++ ) { System.out.println(Thread.currentThread().getName() + ", "+i); try { Thread.sleep(1000); } catch (InterruptedException e) { } } }, "Lambda线程").start();