Lambda表达式不是Java最早使用的,很多语言很早就支持Lambda表达式,例如:C++,C#,Python,Scala等。如果有Python或者Javascript的语言基础,对理解Lambda表达式有很大帮助,可以这么说lambda表达式其实就是实现SAM接口的语法,使得Java也算是支持函数式编程的语言。Lambda写得好可以极大的减少代码冗余,同时可读性也好过冗长的匿名内部类。Lambda表达式,贯彻的是函数式编程思想(函数式思想则尽量忽略面向对象的复杂语法:强调做什么,而不是以什么形式去做,而我们要学习的lambda表达式就是函数式思想的体现)
简单认识Lambda
public class Demo1 {
public static void main(String[] args) {
//最基础的创建线程的方式
/*MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();*/
//进阶版
/*new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello world!");
}
}).start();*/
//进进阶版
new Thread(() -> {System.out.println("hello world!");}).start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("hello world!");
}
}
public class Demo2 {
public static void main(String[] args) {
List<String> list = Arrays.asList("a","b","c","d");
list.forEach((String a)->{
System.out.println(a);
});
list.forEach(a-> System.out.println(a));
list.forEach(System.out::println);
}
}
总结:
lambda表达式的格式:(形式参数)->{代码块/方法体}
· 形式参数:如果有多个参数,参数之间用逗号隔开;没有参数,留空。
· ->;由中划线和大于号组成,固定写法,代表指向动作。
· 代码块:是我们具体实现的事情,理解为方法体
使用
· 定义一个接口AddInter,定义一个抽象方法add(int a, int b)
· 定义一个测试类Test,定义两个方法;
testAdd(AddInter addInter)
主方法来调用testAdd方法
public class Test {
public static void main(String[] args) {
System.out.println(testAdd((int a, int b) -> {
return a + b;
}));
}
static int testAdd(AddInter addInter) {
//模拟从数据库中调用获得两个值
int a = 1;
int b = 2;
return addInter.add(a, b);
}
}
interface AddInter{
int add(int a,int b);
}
Lambda省略规则
· 参数类型可以省略,但是有多个参数的情况下,不能只省略一个。
· 如果参数有且只有一个,那么小括号可以省略。
· 如果代码块只有一条,可以省略大括号和分号,和return关键字。
注意事项
·使用Lambda表达式必须要有接口,并且要求接口中有且仅有一个抽象方法。
·必须有上下文环境,才能推导出Lambda对应的接口
根据局部变量的赋值得知Lambda对应的接口;
Runnable runnable=()->System . out .println("lambda");
根据调用方法的参数得知Lambda对应的接口:
new Threand( () - > System.out.println("lambda") );
Lambda表达式和匿名内部类的区别
Lambda | 匿名内部类 | |
所需类型不同 | 可以是接口,也可以是抽象类,还可以是具体类 | 只能是接口 |
使用类型不同 | 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式 | 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类 |
实现原理不同 | 编译之后,产生一个单独的.class字节码文件 | 编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成 |
体验
在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作。
那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在了相同的方案的时候,那我们还有没有必要重复编写逻辑代码呢?
那肯定是不必要的,这时我们就可以通过方法引用来使用已经存在的方案。
public class Test {
public static void main(String[] args) {
//lambda表达式写法
/*demoPrint((String s) -> {
System.out.println(s);
});*/
//简写
demoPrint(s->System.out.println(s));
//方法引用
demoPrint(System.out::println);
}
static void demoPrint(PrintInter printInter) {
printInter.print("hello world");
}
}
interface PrintInter{
void print(String s);
}
方法引用符
方法引用符“::”:该符号为引用运算符,而它所在的表达式被称为方法引用。
回顾一下我们在体验方法引用中的代码
Lambda表达式:usePrintable(s -> System.out.println(s));
分析:拿到参数s之后通过Lambda表达式,传递给System.out.println方法去处理
方法引用:usePrintable(System.out::println);
分析:直接使用System.out中的println方法来取代Lambda,代码更加的简洁
推导与省略:
- 如果使用Lambda,那么根据“可推导就可省略”的原则,无需指定参数类型,也无需指定的重载形式,它们都是被自动推导。
- 如果使用了方法引用,也是同样可以根据上下文进行推导。
- 方法引用是Lambda的孪生兄弟
Lambda表达式支持的方法引用
- 引用类方法
- 引用对象的实例方法
- 引用类的实例方法
- 引用构造器
引用类方法
引用类方法,其实就是引用类的静态方法。
public class Test {
public static void main(String[] args) {
//Lambda写法
getConverter(s -> Integer.parseInt(s));
//引用类方法
getConverter(Integer::parseInt);
}
static void getConverter(Converter c) {
int number = c.convert("123");
System.out.println(number);
}
}
interface Converter {
int convert(String s);
}
条件:
(1)当Lambda体的实现是通过调用一个现有的方法来完成功能时
(2)要求函数式接口的抽象方法返回值类型与该方法的返回值类型要对应。
(3)要求函数式接口的抽象方法的形参列表与该方法的形参列表对应
引用对象的实例方法
引用对象的实例方法,其实就引用类中的成员方法。
public class Test {
public static void main(String[] args) {
//Lambda简化写法
usePrinter(s -> System.out.println(s.toUpperCase()));
//引用对象的实例方法
PrintString ps = new PrintString();
usePrinter(ps::printUpper);
}
static void usePrinter(Printer p) {
p.printUpperCase("Hello World");
}
}
interface Printer {
void printUpperCase(String s);
}
class PrintString {
//把字符串参数变成大写的数据,然后在控制台输出
public void printUpper(String s) {
String result = s.toUpperCase();
System.out.println(result);
}
}
引用类的实例方法
引用类的实例方法,其实就是引用类中的成员方法。
public class Test {
public static void main(String[] args) {
//Lambda简化写法
getMyString((s,x,y) -> s.substring(x,y));
//引用类的实例方法
getMyString(String::substring);
}
static void getMyString(MyString my) {
String s = my.mySubString("HelloWorld", 2, 5);
System.out.println(s);
}
}
interface MyString {
String mySubString(String s,int x,int y);
}
注意:Lambda表达式被类的实例方法替代的时候,第一个参数作为调用者,后面的参数全部传递给该方法作为参数。
引用构造器
引用构造器,其实就是引用构造方法。
public class Test {
public static void main(String[] args) {
//Lambda简化写法
getStudentBuilder((name,age) -> new Student(name,age));
//引用构造器
getStudentBuilder(Student::new);
}
static void getStudentBuilder(StudentBuilder sb) {
Student s = sb.build("张飞", 20);
System.out.println(s.getName() + "," + s.getAge());
}
}
interface StudentBuilder {
Student build(String name,int age);
}
class Student{
private String name;
private int age;
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;
}
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}