Lambda表达式
从JDK8时引入
函数式编程
函数式编程(Functional programming)是一种思想方式。其忽略面向对象的复杂语法,强调做什么,而不是谁去做。
而面向对象的思想则是:先找对象,让对象做事情。
Lambda表达式的标准格式
Lambda表达式是JDK8开始后的一种新语法形式。
( ) -> {
}
- () 对应着方法的形参
- -> 固定格式
- {} 对应着方法的方法体
注意点:
● Lambda表达式可以用来简化匿名内部类的书写
● Lambda表达式只能简化函数式接口的匿名内部类的写法
● 函数式接口:有且仅有一个抽象方法的接口(抽象类不行)叫做函数式接口,接口上方可以加@FunctionalInterface注解(加了@FunctionalInterface注解后如果定义该接口是不符合函数式接口的规范将会报错)
Lambda应用示例
首先我们定义一个函数式接口ISport,代码:
/**
* 本接口表示会某项运动
* 有且只能有一个待实现的方法,本例中为sport()方法
*/
@FunctionalInterface // 函数式接口的校验注解
public interface ISport {
void sport(); // 接口唯一一个方法
}
接着我们定义一个Person类,它有一个doSport方法需要使用ISport的实例作为参数,我们在调用Person类的doSport方法时,需要传入一个ISport接口的实例,在引入Lambda之前,我们一般会编写一个匿名类,在引入lambda之后,我们可以简化该匿名类的编写方式,代码:
public class Person {
public static void main(String[] args) {
// 内部匿名类的方式实现ISport接口
doSport(new ISport() {
@Override
public void sport() {
System.out.println("去游泳了。");
}
});
// lambda的语法方式实现ISport接口
doSport(() -> {
System.out.println("去做瑜伽了。");
});
}
// 定义一个方法要使用ISport对象为参数
public static void doSport(ISport s){
s.sport();
}
}
// 本段代码运行后输出结果为:
// 去游泳了。
// 去做瑜伽了。
Lambda表达式的简略写法
lambda的省略规则:
总原则是 可推导,则可省略。也即凡是可以推导出来都可以省略,具体为:
- 参数类型可以省略不写。
- 如果只有一个参数,参数类型可以省略,同时括号()也可以省略。
- 如果Lambda表达式的方法体只有一行,大括号,分号,return可以省略不写,但这三者需要同时省略,不能省略其中的一个或两个。
如下代码,我们重写Arrays的sort类,可实现升降序排序:
import java.util.Arrays;
import java.util.Comparator;
public class SortWithDirection {
public static void main(String[] args) {
Integer[] arr = {2, 3, 1, 5, 6, 7, 8, 4, 9};
// 1.匿名类写法
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
// 2.标准lambda语法
Arrays.sort(arr, (Integer o1, Integer o2) ->{
return o1 - o2;
});
// 3.省略参数类型
Arrays.sort(arr, (o1, o2) ->{
return o1 - o2;
});
// 4.省略大括号、分号、return
Arrays.sort(arr, (o1, o2) -> o1 - o2);
}
}
又如,通过字符串的长度对字符串数组进行排序(升序,即短的排前面):
import java.util.Arrays;
/**
* 通过字符串的长度对字符串数组进行排序
*/
public class StringSortByLen {
public static void main(String[] args) {
// 需要排序的字符串数组
String[] arr = {"a","abcd","abc","ab"};
System.out.println(Arrays.toString(arr));
// 重写Arrays.sort方法进行排序,使用lambda简略写法,一行搞定
Arrays.sort(arr, (s1, s2) -> s1.length() - s2.length());
System.out.println(Arrays.toString(arr));
}
}
// 本代码的输出为:
// [a, abcd, abc, ab]
// [a, ab, abc, abcd]
双冒号::操作符写法
这种方式被称为方法引用(Method Reference),它也可以提高lambda表达式的简洁性,它适用于以下场景:当你的lambda表达式只是用来调用一个已存在的方法时,也即你不用在lambda表达式里写自己的业务逻辑,而是单纯调用其他已经存在方法,比如输出一个字符串可以写作System.out::println。根据调用的方式不同可以分为:引用一个静态方法、引用一个特定对象的实例方法、引用一种特定类型的任一对象的实例方法、引用构造方法。
关于方法引用的官方文档地址
https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
方法引用的几种方式,笔者简单的翻译了下:
种类 Kind 语法 Syntax 例子 Examples 引用一个静态方法
Reference to a static method
ContainingClass::staticMethodName
Person::compareByAge
MethodReferencesExamples::appendStrings
引用一个特定对象的实例方法
Reference to an instance method of a particular object
containingObject::instanceMethodName
myComparisonProvider::compareByName
myApp::appendStrings2
引用一种特定类型的任一对象的实例方法
Reference to an instance method of an arbitrary object of a particular type
ContainingType::methodName
String::compareToIgnoreCase
String::concat
引用构造方法
Reference to a constructor
ClassName::new
HashSet::new
请看示例,定义了一个Person类JavaBean,然后尝试调用Person List中的forEach方法,代码如下:
public class Person {
private String ID;
private String name;
private int age;
private float height;
public Person() {
}
public Person(String ID, String name, int age, float height) {
this.ID = ID;
this.name = name;
this.age = age;
this.height = height;
}
/**
* 获取
* @return ID
*/
public String getID() {
return ID;
}
/**
* 设置
* @param ID
*/
public void setID(String ID) {
this.ID = ID;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return height
*/
public float getHeight() {
return height;
}
/**
* 设置
* @param height
*/
public void setHeight(float height) {
this.height = height;
}
public String toString() {
return "Person{ID = " + ID + ", name = " + name + ", age = " + age + ", height = " + height + "}";
}
public void printPerson(){
System.out.println(this.toString());
}
}
Lambda的写法如下,包含双引号操作符:
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class LambdaTest {
public static void main(String[] args) {
Person p1 = new Person("001","王伟",45,163.2F);
Person p2 = new Person("002","张小美",31,158.6F);
Person p3 = new Person("003","刘睿国",53,178.0F);
Person p4 = new Person("004","费香香",23,162.0F);
Person p5 = new Person("005","陈希",25,170.5F);
Person p6 = new Person("006","金奇辉",41,193.2F);
Person[] pArr = {p1, p2, p3, p4, p5};
List<Person> pList = Arrays.asList(pArr);
// 1.匿名类的写法
pList.forEach(new Consumer<Person>() {
@Override
public void accept(Person person) {
System.out.println(person.getName());
}
});
System.out.println("----------------------------");
// 2.Lambda标准写法
pList.forEach((Person person) -> {
System.out.println(person.toString());
});
System.out.println("----------------------------");
// 3.Lambda简略写法1:省略参数类型
pList.forEach((person)-> {
person.printPerson();
});
System.out.println("----------------------------");
// 4.Lambda简略写法2:省略参数类型和大括号、分号、return
pList.forEach((person)->
person.printPerson()
);
System.out.println("----------------------------");
// 简略成一行:pList.forEach((person)->System.out.println(person.getName()));
// 5.Lambda简略写法4:省略参数类型和大括号、分号、return,使用::双冒号调用方法
// 此调用应属于:引用一种特定类型的任一对象的实例方法 Reference to an instance method of an
// arbitrary object of a particular type
pList.forEach(Person::printPerson);
}
}
Lambda小结
1、Lambda表达式的基本作用?
简化函数式接口的匿名内部类的写法。
2、Lambda表达式有什么使用前提?
必须是接口的匿名内部类,接口中只能有一个抽象方法
3、Lambda的好处?
Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码,它可以写出更简洁、更灵活的代码,作为一种更紧凑的代码风格,使Java语言表达能力得到了提升。
笔者短评
笔者认为编码者在工作中如果盲目的引入Lambda,反而会造成代码可读性的降低,对于代码的后期维护可能并不友好。另外对于Java严谨的语法来说,简略的Lambda语句是对Java豪华型的一种挑战。笔者更倾向于使用匿名类。