# 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`
使用较少,但在使用时,抽象方法必须要有参数指定数组大小