合规国际互联网加速 OSASE为企业客户提供高速稳定SD-WAN国际加速解决方案。 广告
# Lambda表达式 [TOC] ## 导学 Lambda表达式是Java 8中的一个新特性,可以用于取代绝大部分的匿名内部类,写出更优雅的Java代码。尤其可以用在集合的遍历与其他一些集合操作中,可以极大的简化代码结构。 Lambda 表达式,也可称为闭包,允许把函数作为一个方法的参数(函数作为参数传递进方法中)。 ~~~Java Collections.sort(list,(o1, o2) -> { double salary1 = o1.getSalary(); double salary2 = o2.getSalary(); if(salary1 > salary2) { return 1; } else if(salary1 == salary2) { return 0; } else { return -1; } }); ~~~ >[info] 在sort方法中,需要传入两个参数,第一个是需要排序的集合,第二个排序规则对象。但上述代码中,排序规则对象被简化成了一个类似于js中的箭头函数。此时,形成了将一个方法作为参数传入到另一个方法中的现象。 ## Lambda表达式的使用 ### Lambda表达式的使用限制 Lambda表达式主要用于替代接口的实现类,所以对于接口是有要求的。要求该接口中只能有一个被实现的方法,但并不是说接口中只能有一个方法。 例如在jdk 8中,接口中可以存在静态方法和默认方法,但是如果只存在一个需要被实现的方法,则该接口的实现类就可以用Lambda表达式代替。 ~~~Java @FunctionalInterface public interface IRun { void run(); default void jump() { System.out.println("跳"); } static void rap() { System.out.println("rap"); } } ~~~ >[success]`@FunctionalInterface`用于限制接口中只能有一个用于被实现的方法。它修饰的接口称之为函数式接口。 ### Lambda表达式的基本代码实现 Lambda虽然用于接口实现类,但从表现形式上来说,更类似于替代接口中唯一需要被实现的那个方法。所以,lambda表达式使用`() -> {}`的形式为基本语法。其中`()`用来描述参数列表,`{}`用来描述方法体,`->`作为lambda表达式的运算符,读作`goes to`。 先新建几个接口: ~~~Java /** * 无参无返回值 */ public interface NoReNoPa { void method(); } /** * 单参无返回值 */ public interface NoReOnePa { void method(int a); } /** * 多参无返回值 */ public interface NoReMuPa { void method(int a, int b); } /** * 无参有返回值 */ public interface OneReNoPa { int method(); } /** * 单参有返回值 */ public interface OneReOnePa { int method(int a); } /** * 多参有返回值 */ public interface OneReMuPa { int method(int a, int b); } ~~~ 接下来,看看Lambda的具体使用 ~~~java public class LambdaTest { public static void main(String[] args) { //如果使用lambda的话,需要去描述接口中的方法 /*NoReNoPa nrnp = new NoReNoPa() { @Override public void method() { } };*/ //无参无返回值 NoReNoPa nrnp = () -> { System.out.println("无参无返回值"); }; nrnp.method(); //单参无返回值 NoReOnePa nrop = (int a) -> { System.out.println("单参无返回值,参数为:" + a); }; nrop.method(5); //多参无返回值 NoReMuPa nrmp = (int a, int b) -> { System.out.println("多参无返回值,参数为:a=" + a + ",b=" + b); }; nrmp.method(2, 3); //无参有返回值 OneReNoPa ornp = () -> { return 5; }; System.out.println(ornp.method()); //单参有返回值 OneReOnePa orop = (int x) -> { return x; }; System.out.println(orop.method(6)); //多参有返回值 OneReMuPa ormp = (int a, int b) -> { return a + b; }; System.out.println(ormp.method(3, 4)); } } ~~~ ### Lambda表达式的简化操作 在Lambda表达式中,可以遵循一定的简化规则,对代码进行简化操作。 1. 简化参数类型,可以不写参数类型,但是必须要求所有的参数都不写参数类型 ~~~Java //多参无返回值 NoReMuPa nrmp = (a, b) -> { System.out.println("多参无返回值,参数为:a=" + a + ",b=" + b); }; nrmp.method(2, 3); ~~~ 2. 简化参数的小括号,如果只有一个参数,则可以省略小括号。无参和多参的情况下,不可省略 ~~~Java //单参无返回值 NoReOnePa nrop = a -> { System.out.println("单参无返回值,参数为:" + a); }; nrop.method(5); ~~~ 3. 简化方法体大括号,如果方法体中,只有一条语句。则可以省略方法体大括号 ~~~Java //单参无返回值 NoReOnePa nrop = a -> System.out.println("单参无返回值,参数为:" + a); nrop.method(5); ~~~ 4. 简化return语句,如果方法体中只有一条语句,并且是`return`语句,则可以省略大括号和`return`关键字 ~~~Java //单参有返回值 OneReOnePa orop = x -> x + 1; System.out.println(orop.method(6)); ~~~ ### Lambda表达式的应用 如果在方法中有接口类型作为参数,并且该接口中只有一个需要被实现的方法,那么在传入参数时,可以传入通过Lambda描述的方法执行体。 ~~~Java public class LambdaTest { public static void print(OneReMuPa ormp, int a , int b) { if(ormp.method(a, b) < 10) { System.out.println("小于10"); } else { System.out.println("大于10"); } } public static void main(String[] args) { print((a , b) -> a + b, 3, 5); } } ~~~ ## 方法引用 ### 方法引用的介绍 方法引用在Lambda表达式的基础上,继续对代码进行简化。方法引用可以认为是Lambda表达式的一种特殊形式,Lambda表达式可以让开发者自定义抽象方法的实现代码,方法引用则可以让开发者直接引用已存在的实现方法。 示例: ~~~Java public class LambdaTest { public static void print(OneReMuPa ormp, int a , int b) { if(ormp.method(a, b) < 10) { System.out.println("小于10"); } else { System.out.println("大于10"); } } public static void main(String[] args) { //print((a , b) -> ORMP.add(a, b), 3, 5); print(ORMP::add, 3, 5);//方法引用 } } class ORMP { public static int add(int x, int y) { return x + y; } } ~~~ 方法引用使用`::`来表达引用者对方法的指向,方法引用并不是什么很神秘的东西,而是引用别人所写好的方法,来代替自己要写的方法体。 **注意:方法引用有几个限制要求:** >[danger] >1. 方法引用也是用来代替接口中抽象方法的实现的,所以该接口必须也是`FunctionalInterface`类型的接口 >2. 在抽象方法的实现中,只会调用其他方法,没有其余操作 >3. 调用的方法需要和抽象方法,返回值一致,参数列表一致。 ### 方法引用的类型 1. 在方法引用中,使用类名引用静态方法`Class::staticMethod` 示例代码如介绍部分所示 2. 在方法引用中,使用对象引用成员方法`obj::method` ~~~Java public class LambdaTest { public static void print(OneReMuPa ormp, int a , int b) { if(ormp.method(a, b) < 10) { System.out.println("小于10"); } else { System.out.println("大于10"); } } public static void main(String[] args) { print((a, b) -> { ORMP op = new ORMP(); return op.add(a, b); }, 3, 5); ORMP op = new ORMP(); print(op::add, 3, 5);//正确 print(new ORMP()::add, 3,12);//正确 NoReOnePa nrop = System.out::println; nrop.method(5);//System.out.println(5); } } class ORMP { public int add(int x, int y) { return x + y; } } ~~~ 3. 在方法引用中,有一种特殊的引用,使用类名引用成员方法`Class::method`。引用的方法参数列表和需要实现的方法不一致。但只要需要实现的方法参数类型相同,则可以引用这参数列表不同的方法,而且这时候,反而不可以使用对象名引用方法,需要用类名引用该方法。这是因为在抽象方法的实现中,其中一个参数将作为引用方法的调用者。 ~~~Java public class LambdaTest { public static void main(String[] args) { IStudent is = (stu1, stu2) -> stu1.function(stu2); IStudent is2 = Student::function;//将默认使用第一个参数去引用方法 } } interface IStudent { void method(Student stu1, Student stu2); } class Student { public String name; public int age; public void function(Student stu) { if(this.age > stu.age) { System.out.println("年龄大"); } else { System.out.println("小一些"); } } } ~~~ 4. 在方法引用中,调用构造器的写法为类名引用new `Class::new` ~~~Java public class LambdaTest { public static void main(String[] args) { ICreate ic = new ICreate() { @Override public Student method() { return new Student(); } }; ICreate ic1 = () -> new Student(); ICreate ic2 = Student::new; } } interface ICreate { Student method(); } class Student { public String name; public int age; } ~~~ 5. 在方法引用中,构建数组使用 `Class[]::new` 使用较少,但在使用时,抽象方法必须要有参数指定数组大小