1.Lambda基础
Lambda表达式的作用就是:减少代码的冗余量,相对于内部匿名类可读性增强
1.1普通函数定义:
// 返回类型 方法名 参数列表 方法体
int add (int a, int b) { return a+b; }
1.2省略过程
- step1:返回类型和方法名直接省略,使用lambda操作符 -> 连接
(int a, int b) -> { return a+b; }
- step2:参数类型省略,并且要省略全都得省略
(a, b) -> { return a+b; }
- step3:如果参数只有一个,则省略小括号,以新函数为例:
int pow(int x) { return x*x; }
x -> { return x*x; }
- step4:如果方法体中只有一句代码,则省略大括号和分号
(a, b) -> return a+b;
- step5:如果仅剩的一句代码是返回语句,则return必须跟随大括号一起省略
(a, b) -> a+b
- step6:如果无参数,则保留一对小括号,以新函数为例:
int get100() { return 100; }
() -> 100
2.函数式接口
有且只有一个抽象方法,但是可以有多个非抽象方法的接口,就是函数式接口。
2.1Lambda表达式和函数式接口的关系
Lambda表达式就是专门给函数式接口的形参或变量赋值用的。
2.1.1举例:调用自定义函数式接口
package test;
public class lambda {
@FunctionalInterface // 该注解作用:标明为函数式接口,编译阶段会检查其内部是否有且只有一个抽象方法
public interface MyFunctionalInterface{ // 自定义函数式接口
void method(); //抽象方法
}
// 定义一个“参数为函数式接口”的方法
public static void doSomething(MyFunctionalInterface functionalInterface) {
functionalInterface.method();//调用函数式接口里的方法
}
// 定义一个“返回值为函数式接口”的方法,普通方法实现
public static MyFunctionalInterface getSomething(){
return new MyFunctionalInterface() {
@Override
public void method() {
System.out.println("返回值为一个匿名之类,普通方法实现");
}
};
}
// 定义一个“返回值为函数式接口”的方法,Lambda表达式方式实现
public static MyFunctionalInterface getSomethingLambda(){
return () -> System.out.println("返回值为一个匿名之类,Lambda表达式方式实现");
}
public static void main(String[] args){
// 因为是static的方法,所以可以直接调用
// 普通方式调用函数式接口并重写抽象方法
doSomething(new MyFunctionalInterface() {
@Override
public void method() {
System.out.println("111");
}
});
// Lambda表达式方式调用函数式接口并重写抽象方法
doSomething(()->System.out.println("222"));
MyFunctionalInterface m1 = getSomething();
m1.method();
MyFunctionalInterface m2 = getSomethingLambda();
m2.method();
}
}
额外的小问题:抽象类不是不能实例化吗?为什么上面“普通方式调用”的代码实例化抽象类了?
答:上面的代码中并不是实例化抽象类,而是创建了一个匿名子类,实例化的是匿名子类,并不是抽象类。
2.1.2举例:调用系统提供的函数式接口
package test;
public class lambda {
public static void doRunnable(Runnable runnable){
runnable.run();
}
public static void testRunnable(){
System.out.println("开始调用");
doRunnable(()->System.out.println("调用第1个Runnable"));
doRunnable(()->System.out.println("调用第2个Runnable"));
System.out.println("结束调用");
}
public static void testThread() {
System.out.println("开始调用线程");
new Thread(()-> System.out.println("调用第1个线程")).run();
new Thread(()-> System.out.println("调用第2个线程")).run();
System.out.println("结束调用线程");
}
public static void main(String[] args) throws InterruptedException {
testRunnable();
System.out.println("--------------------");
testThread();
}
}
2.1.3总结
Lambda表达式的使用场景就是“创建函数式接口的(匿名)子类,并将(匿名)子类作为参数或返回值传递时,可以使用Lambda表达式简写其重写的方法”
这句话完全可以换成“继承并实例化函数式接口,然后将实例化的对象作为参数或返回值传递时,可以使用Lambda表达式简写其重写的方法”,但是这不如上面那句好,因为上面描述的场景比下面这句描述的场景更具体。
2.3Java内置的四大函数式接口
函数式接口 | 特点 | 举例 |
---|---|---|
消费型Consumer | 有参数无返回值 | |
供给型Supplier | 无参数有返回值 | |
判断型Predicate | 返回值为布尔类型 | |
功能型Function<T,R> | 有参数有返回值 |
3.方法引用
如果某个方法的签名和某个函数式接口中的抽象方法恰好一致,那么可以直接使用该方法的引用。
package test;
public class lambda {
@FunctionalInterface // 该注解作用:标明为函数式接口,编译阶段会检查其内部是否有且只有一个抽象方法
public interface MyFunctionalInterface{ // 自定义函数式接口
double method(double a,double b); //抽象方法
}
// 定义一个“参数为函数式接口”的方法
public static double doSomething(MyFunctionalInterface functionalInterface) {
return functionalInterface.method(3,2);//调用函数式接口里的方法
}
// 自定义定义一个"方法签名和函数式接口相同"的方法
public static double add(double x,double y){
return x + y;
}
// Math类中的pow方法的方法签名就是 double (num1,num2),恰好和MyFunctionalInterface中的method一致
public static void main(String[] args){
double d1 = doSomething(lambda::add); // 调用自定义函数式接口,使用自定义add方法的引用
double d2 = doSomething(Math::pow); // 调用自定义函数式接口,使用Math类中pow方法的引用
System.out.println(d1);
System.out.println(d2);
}
}