1. Lambda表达式的简介
1.1. Lambda表达式的概念
Lambda表达式,是java8的一个新特性,也是java8中最值得学习的新特性之一。
Lambda表达式,从本质来讲,是一个匿名函数。可以使用这个匿名函数,实现接口中的方法,对接口进行非常简洁的实现,从而简化代码。
1.2. Lambda表达式的使用场景
通常来讲,使用Lambda表达式。是为了简化接口实现的。
关于接口实现,可以有多多种方式,例如:设计接口的实现类,使用匿名内部类,但是Lambda表达式,比这两种方式都简单。
1.3. Lambda表达式对接口的要求
虽然说,Lambda表达式可以在一定程度上简化接口的实现,但是,并不是所有的接口都可以使用Lambda表达式来简洁实现。Lambda表达式毕竟只是一个匿名函数方法。当实现的接口中的方法过多或者过少的时候,Lambda表达式都是不适用的。
Lambda表达式只能实现函数式接口。
1.4. 函数式接口
1.4.1. 基础概念
如果说,一个接口中,要求实现类必须实现的抽象方法,有且只有一个!这样的接口,就是函数式接口。
实例:
//这个接口中,有且只有一个方法,是实现类必须实现的,因此是一个函数式接口
interface Test1 {
void test();
}
//这个接口中,实现类必须实现的方法有两个,因此不是函数式接口
interface Test2{
void test1();
void test2();
}
//这个接口中,实现类必须要实现的方法有零个,因此不是一个函数式接口
interface Test3{
}
//这个接口中,虽然没有定义任何的方法,但是可以从父接口中继承到一个抽象方法,是一个函数式接口
interface Test4 extends Test1{
}
//这个接口,虽然里面定义了两个方法,但是default方法子类不是必须实现的。
//因此,实现类实现这个接口的时候,必须实现的方法只有一个,是一个函数式接口
interface Test5{
void test5();
default void test(){}
}
//这个接口中的toString方法,是Object类中定义的方法。
//此时,实现类在实现接口时,toString方法可以不重写,因为可以从父类Object中继承的
//此时,实现类,在实现接口的时候,有且只有一个方法必须实现重写,是一个函数接口
interface Test6{
void test6();
String toString();
}
//不是一个函数式接口
interface Test7{
}
//是一个函数接口
interface test8{
void test();
default void test1(){};
static void test2(){};
String toString();
}
1.4.2. @FunctionalInterface
是一个注解,用在接口之前,判断这个接口是不是一个函数式接口,如果是函数式接口,没有任何问题,如果不是函数式接口,则会报错,功能类似于@Override
实例:
@FunctionalInterface
interface test8{
void test();
default void test1(){};
static void test2(){};
String toString();
}
2. Lambda表达式语法
准备几个函数式接口
/**
* @author qwy
* @create 2021-03-31 16:24
**/
//无返回值,无参的函数接口
@FunctionalInterface
interface Test1{
void test();
}
//无返回值,一个参数的函数接口
@FunctionalInterface
interface Test2{
void test(int age);
}
//无返回值,多个参数的函数接口
interface Test3{
void test(int age,String name);
}
//有返回值,无参函数接口
interface Test4{
int test();
}
//有返回值,一个参数的函数接口
interface Test5{
String test(String name);
}
//有返回值,多个参数的函数接口
interface Test6{
String test(String name,int age);
}
2.2. Lambda表示的基础语法
Lambda表达式,其实本质就是一个匿名函数,因此在写Lambda表达式的时候,不需要关心方法名是什么。实际上,我们在写Lambda表达式的时候,也不需要关心返回类型。
只需要关心两部分内容即可:参数列表和方法体。
Lambda表达式的基础语法:
(参数)->{
方法体;
}
参数部分:方法的参数列表,要求和实现的接口中的方法参数部分一致,包括参数的数量和类型。
方法体部分:方法的实现部分,如果接口中定义的方法有返回值,则在实现的时候,注意返回值的返回
->:分割参数部分和方法体部分。
实例:
public class TestFunctionalInterface {
public static void main(String[] args) {
//实现无返回值,无参数的函数式接口
Test1 t1=()->{
System.out.println("无返回,无参数的函数");
};
t1.test();
System.out.println("*******************");
//实现无返回值,一个参数的函数式接口
Test2 t2=(int age)->{
System.out.println("无返回,一个参数的函数");
System.out.println("age = " + age);
};
t2.test(12);
System.out.println("*******************");
//实现无返回值,多个参数的函数式接口
Test3 t3=(int age,String name)->{
System.out.println("无返回值,多个参数的函数");
System.out.println("age = " + age);
System.out.println("name = " + name);
};
t3.test(12,"张三");
System.out.println("*******************");
//实现有返回值,无参数的函数式接口
Test4 t4=()->{
System.out.println("有返回值,无参数函数");
return 12;
};
int age = t4.test();
System.out.println("age = " + age);
System.out.println("*******************");
//实现有返回值,一个参数的函数式接口
Test5 t5=(String name)->{
System.out.println("有返回值,一个参数的函数");
return name;
};
String name = t5.test("李四");
System.out.println("name = " + name);
System.out.println("*******************");
//实现有返回值,多个参数的函数式接口
Test6 t6= (String n, int a)->{
System.out.println("有返回值,多个参数的函数");
return "name->"+name+",age->"+age;
};
String str = t6.test("王五", 13);
System.out.println("str = " + str);
}
}
运行结果:
无返回,无参数的函数
#*******************
无返回,一个参数的函数
age = 12
#*******************
无返回值,多个参数的函数
age = 12
name = 张三
#*******************
有返回值,无参数函数
age = 12
#*******************
有返回值,一个参数的函数
name = 李四
#*******************
有返回值,多个参数的函数
str = name->李四,age->12
2.2. lambda表达式语法进阶
在上面例子中,的确可以使用Lambda表达式实现接口,但是依然不够简洁,有简化的空间。
2.2.1. 参数部分的精简
参数的类型:
由于在接口的方法中,已经定义了每一个参数的类型是什么,而且在使用Lambda表达式实现接口的时候,必须要保证参数的数量和类型需要和接口中的方法保持一致,因此,Lambda表达式中参数类型可以省略不写。
注意:如果需要省略参数的类型,要保证:每一个参数的类型都必须省略不写,绝对不能出现有的参数类型省略了,有的参数类型没有省略。
实例:
public class TestFunctionalInterface {
public static void main(String[] args) {
//实现无返回值,无参数的函数式接口
Test1 t1=()->{
System.out.println("无返回,无参数的函数");
};
t1.test();
System.out.println("*******************");
//实现无返回值,一个参数的函数式接口
Test2 t2=(age)->{
System.out.println("无返回,一个参数的函数");
System.out.println("age = " + age);
};
t2.test(12);
System.out.println("*******************");
//实现无返回值,多个参数的函数式接口
Test3 t3=(age,name)->{
System.out.println("无返回值,多个参数的函数");
System.out.println("age = " + age);
System.out.println("name = " + name);
};
t3.test(12,"张三");
System.out.println("*******************");
//实现有返回值,无参数的函数式接口
Test4 t4=()->{
System.out.println("有返回值,无参数函数");
return 12;
};
int age = t4.test();
System.out.println("age = " + age);
System.out.println("*******************");
//实现有返回值,一个参数的函数式接口
Test5 t5=( name)->{
System.out.println("有返回值,一个参数的函数");
return name;
};
String name = t5.test("李四");
System.out.println("name = " + name);
System.out.println("*******************");
//实现有返回值,多个参数的函数式接口
Test6 t6= (n, a)->{
System.out.println("有返回值,多个参数的函数");
return "name->"+name+",age->"+age;
};
String str = t6.test("王五", 13);
System.out.println("str = " + str);
}
}
运行结果:与上例无异。
参数的小括号:
如果方法的参数列表中参数数量有且只有一个,此时,参数列表的小括号是可以省略不写的。
注意:
1.只有当参数数量是一个时候,多了,少了,都不能省略
2.省略小括号的同时,必须省略参数的类型
public class TestFunctionalInterface {
public static void main(String[] args) {
//不能省略小括号
Test1 t1=()->{
System.out.println("无返回,无参数的函数");
};
t1.test();
System.out.println("*******************");
//能省略小括号
Test2 t2 = age -> {
System.out.println("无返回,一个参数的函数");
System.out.println("age = " + age);
};
t2.test(12);
System.out.println("*******************");
//不能省略小括号
Test3 t3=(age,name)->{
System.out.println("无返回值,多个参数的函数");
System.out.println("age = " + age);
System.out.println("name = " + name);
};
t3.test(12,"张三");
System.out.println("*******************");
//不能省略小括号
Test4 t4=()->{
System.out.println("有返回值,无参数函数");
return 12;
};
int age = t4.test();
System.out.println("age = " + age);
System.out.println("*******************");
//能省略小括号
Test5 t5= name ->{
System.out.println("有返回值,一个参数的函数");
return name;
};
String name = t5.test("李四");
System.out.println("name = " + name);
System.out.println("*******************");
//不能省略小括号
Test6 t6= (n, a)->{
System.out.println("有返回值,多个参数的函数");
return "name->"+name+",age->"+age;
};
String str = t6.test("王五", 13);
System.out.println("str = " + str);
}
}
运行结果:与上例无异。
2.2.2. 方法体部分的精简
方法体大括号的精简:
当一个方法体中的逻辑,有且只有一句的情况下,大括号可以省略。
public class TestFunctionalInterface {
public static void main(String[] args) {
//可以省略方法体
Test1 t1=()-> System.out.println("无返回,无参数的函数");
t1.test();
System.out.println("*******************");
//不可以省略方法体
Test2 t2 = age -> {
System.out.println("无返回,一个参数的函数");
System.out.println("age = " + age);
};
t2.test(12);
System.out.println("*******************");
//不可以省略方法体
Test3 t3=(age,name)->{
System.out.println("无返回值,多个参数的函数");
System.out.println("age = " + age);
System.out.println("name = " + name);
};
t3.test(12,"张三");
System.out.println("*******************");
//不可以省略方法体
Test4 t4=()->{
System.out.println("有返回值,无参数函数");
return 12;
};
int age = t4.test();
System.out.println("age = " + age);
System.out.println("*******************");
//不可以省略方法体
Test5 t5= name ->{
System.out.println("有返回值,一个参数的函数");
return name;
};
String name = t5.test("李四");
System.out.println("name = " + name);
System.out.println("*******************");
//不可以省略方法体
Test6 t6= (n, a)->{
System.out.println("有返回值,多个参数的函数");
return "name->"+name+",age->"+age;
};
String str = t6.test("王五", 13);
System.out.println("str = " + str);
}
}
运行结果:参照上例。
return 的精简:
如果一个方法中唯一的一条语句是一个返回语句,此时省略大括号的同时,也必须省略return。
实例:
public class TestFunctionalInterface {
public static void main(String[] args) {
//可以省略方法体
Test1 t1=()-> System.out.println("无返回,无参数的函数");
t1.test();
System.out.println("*******************");
//不可以省略方法体
Test2 t2 = age -> {
System.out.println("无返回,一个参数的函数");
System.out.println("age = " + age);
};
t2.test(12);
System.out.println("*******************");
//不可以省略方法体
Test3 t3=(age,name)->{
System.out.println("无返回值,多个参数的函数");
System.out.println("age = " + age);
System.out.println("name = " + name);
};
t3.test(12,"张三");
System.out.println("*******************");
//省略方法体
Test4 t4=()-> 12;
int age = t4.test();
System.out.println("age = " + age);
System.out.println("*******************");
//省略方法体
Test5 t5= name -> name;
String name = t5.test("李四");
System.out.println("name = " + name);
System.out.println("*******************");
//省略方法体
Test6 t6= (n, a)-> "name->"+name+",age->"+age;
String str = t6.test("王五", 13);
System.out.println("str = " + str);
}
}
运行结果:
无返回,无参数的函数
#*******************
无返回,一个参数的函数
age = 12
#*******************
无返回值,多个参数的函数
age = 12
name = 张三
#*******************
age = 12
#*******************
name = 李四
#*******************
str = name->李四,age->12
3.函数的引用
Lambda表达式是为了简化接口的实现的,在Lambda表达式中,不应该出现比较复杂的逻辑,如果在Lambda表达式中出现了过于复杂的逻辑,会对程序的可读性造成非常大的影响。如果在Lambda表达式中需要处理的逻辑比较复杂。一般情况下会单独的写一个方法。在Lambda表达式中直接引用这个方法即可。
引用函数:引用一个已经存在的方法,使其替代Lambda表达式完成接口的实现。
实例:
package com.qwy;
/**
* @author qwy
* @create 2021-03-31 20:36
**/
//定义函数式接口
interface Test1{
int add(int a,int b);
}
//定义普通类,用来被接口函数调用
class MyTest{
public int save(int a,int b){
if(a>b){
return a+b;
}else if(a<b){
return a-b;
}else{
return a;
}
}
}
public class TestLambda {
public static void main(String[] args) {
Test1 t1=(a,b)->new MyTest().save(a,b);
int rs = t1.add(3, 4);
System.out.println("rs = " + rs);
}
}
运行结果:
rs = -1
3.1. 静态方法的引用
语法:
类::静态方法
注意:
1.在引用的方法后面,不要添加小括号
2.引用的这个方法,参数(数量,类型)和返回值,必须和接口中定义的方法一致。
实例:
package com.qwy;
/**
* @author qwy
* @create 2021-03-31 20:36
**/
//定义函数式接口
interface Test1{
int add(int a,int b);
}
//定义普通类,用来被接口函数调用
class MyTest{
//将方法静态化
public static int save(int a,int b){
if(a>b){
return a+b;
}else if(a<b){
return a-b;
}else{
return a;
}
}
}
public class TestLambda {
public static void main(String[] args) {
//引用静态方法:注意 //静态函数引用:注意不要写()参数列表
Test1 t1=MyTest::save;
int rs = t1.add(3, 4);
System.out.println("rs = " + rs);
}
}
运行结果:
rs = -1
注意://静态函数引用:注意不要写()参数列表
3.2. 非静态方法的引用
语法:
对象::非静态方法
注意:
在引用的方法后面,不要加小括号。
引用的这个方法,参数(个数,类型)和返回值,必须和函数接口定义的一致。
实例:
package com.qwy;
/**
* @author qwy
* @create 2021-03-31 20:36
**/
//定义函数式接口
interface Test1{
int add(int a,int b);
}
//定义普通类,用来被接口函数调用
class MyTest{
//分静态的方法
public int save(int a,int b){
if(a>b){
return a+b;
}else if(a<b){
return a-b;
}else{
return a;
}
}
}
public class TestLambda {
public static void main(String[] args) {
Test1 t1=new MyTest()::save;
//以下方法也可以
//Test1 t1=(a,b)->new MyTest().save(a,b);
int rs = t1.add(3, 4);
System.out.println("rs = " + rs);
}
}
运行结果:
rs = -1
注意:引用非静态的方法有两种写法:
1.Test1 t1=new MyTest()::save;
2.Test1 t1=(a,b)->new MyTest().save(a,b);
都可以
3.3. 构造方法的引用
使用场景:
如果某一个函数式接口中定义的方法,仅仅为了得到一个类的对象,此时我们就可以使用构造方法的引用。来简化这个方法的实现。
语法:
类名::new
注意:
可以通过接口中的方法的参数,区分引用不同的构造方法。
实例:
package com.qwy2;
/**
* @author qwy
* @create 2021-03-31 21:06
**/
interface MyInferface1{
Person get();
}
interface MyInferface2{
Person get(String name,int age);
}
interface MyInferface3{
Person get(int id,String name,int age);
}
class Person{
private int id;
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class TestLambda {
public static void main(String[] args) {
MyInferface1 m1=Person::new;
Person p1 = m1.get();
System.out.println("p1 = " + p1);
MyInferface2 m2=Person::new;
Person p2 = m2.get("张三", 12);
System.out.println("p2 = " + p2);
MyInferface3 m3=Person::new;
Person p3 = m3.get(1, "李四", 13);
System.out.println("p3 = " + p3);
}
}
运行结果:
p1 = Person{id=0, name=‘null’, age=0}
p2 = Person{id=0, name=‘张三’, age=12}
p3 = Person{id=1, name=‘李四’, age=13}
未完待续