首先先说明一下,Lambda是java提供的,Spring webFlux编程中要用大量的lambda表达式完成编码,所以这里先简要介绍下Lambda
编程类型对比
函数式编程:实现功能并不关心具体实现细节(大部分jdk提供)
命令式编程:程序描述具体实现细节
Lambda表达式介绍
参数列表 -> 代码块
举例:
() -> System.out.println("ok");
返回值是Runnable接口类型
相同的一个lambda表达式调用两次返回结果不相同。
函数接口
为了声明lambda表达式入参和返回值类型,定义的接口。只能有一个方法。
@FunctionalInterface
interface interface1 {
int doubleNum(int i);
//默认方法,如果继承中含有二义性,选择继承那个方法。
default int add(int x, int y){
return x+y;
}
}
Interface1 i1 = (i) -> i * 2;
//调用定义的函数
System.out.println(i1.doubleNum(20));
//调用默认的函数
System.out.println(i1.add(3, 7));
本文所写代码都是在jdk8环境下完成。
代码:说明方法引用的几种方法
class Dog{
/**
* JDK会默认把当前实例传入到非静态方法,参数名为this,位置是第一个
* @param args
*/
private String name = "哮天犬";
private int food = 10;
public Dog(){
}
public Dog(String name){
this.name = name;
}
/**
* 静态方法
*/
public static void bark(Dog dog){
System.out.println(dog + "叫了");
}
public int eat(int num){
System.out.println("吃了" + num + "");
this.food -= num;
return this.food;
}
@Override
public String toString() {
return this.name;
}
}
public class MethodReference {
public static void main(String[] args) {
//消费函数lambda
Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("输入的数据");
//方法引用
Consumer<String> consumer1 = System.out::println;
consumer1.accept("输入的数据");
//静态方法的引用,使用类名引用
Consumer<Dog> consumer2 = Dog::bark;
Dog dog = new Dog();
consumer2.accept(dog);
//非静态方法,使用实例对象引用
//指定参数和返回值类型
// Function<Integer, Integer> function = dog::eat;
//入参和返回值类型相同
UnaryOperator<Integer> function = dog::eat;
System.out.println("还剩下" + function.apply(2) + "斤");
//使用特定结构体
IntUnaryOperator function1 = dog::eat;
//如果实例是null,不会影响函数结果
// dog = null; //屏蔽,下面还会用到
System.out.println("还剩下" + function1.applyAsInt(2) + "斤");
//非静态方法,使用类名引用,Dog:eat;
BiFunction<Dog, Integer, Integer> eatFunction = Dog::eat;
System.out.println(eatFunction.apply(dog, 3));
//构造函数的引用
Supplier<Dog> supplier = Dog::new;
System.out.println("创建了新对象: " + supplier.get());
//带参数的构造函数的方法引用
Function<String, Dog> function3 = Dog::new;
System.out.println("创建了新对象:"+ function3.apply("旺财"));
}
}
//输出结果:
//输入的数据
//输入的数据
//哮天犬叫了
//吃了2
//还剩下8斤
//吃了2
//还剩下6斤
//吃了3
//3
//创建了新对象: 哮天犬
//创建了新对象:旺财
lambda 返回值类型推断
/**
* 类型推断
*/
@FunctionalInterface
interface IMath{
int add(int x, int y);
}
public class TypeDemo {
public static void main(String[] args) {
//变量类型定义
IMath lambda = (x,y) -> x+y;
//数组里
IMath[] lambda1 = {(x,y)->x+y};
//强转
Object lambda2 = (IMath)(x,y)->x+y;
//通过返回类型
IMath creatLambda = createLambda();
TypeDemo typeDemo = new TypeDemo();
//如果存在多个重载方法参数是接口,可以使用强转
typeDemo.test((x,y)->x+y);
}
public void test(IMath math){
}
public static IMath createLambda(){
return (x,y) -> x+y;
}
}
变量引用
在将集合传入内部函数之后,不要修改集合变量,容易产生歧义。即外部改变了之后导致内部和外部引用指向的不是同一个对象。
级联表达式和柯里化
/**
* 级联表达式和柯里化
*/
public class CurryDemo {
public static void main(String[] args) {
//实现级联表达式
Function<Integer, Function<Integer, Integer>> function = x->y->x+y;
System.out.println(function.apply(2).apply(3));
//柯里化就是把多个参数的函数转化为只有一个参数的函数
//目的:函数标准化,便于重复使用
//高阶函数:返回函数的函数
}
}
lambda底层实现原理
/**
* lambda底层实现原理
*
* 1. 编译器会为每一个lambda表达式生成一个方法
* 方法名是lambda$0,1,2,3,但方法引用的表达式不会生成方法。
* 2. 在lambda地方会产生一个invokeDynamic指令,这个指令会调用
* bootstrap(引导)方法,bootstrap方法会指向自动生成的lambda$0
* 方法或者方法引用的方法。
* 3. bootstrap方法使用上是调用了LambdaMetafactory.metafactory静态方法
* 该方法返回了CallSite(调用站点),里面包含了MethodHandle(方法句柄)
* 也就是最终调用的方法。
* 4. 引导方法只会调用一次。
*
* 自动生成的方法:
* 1. 输入和输出和lambda一致
* 2. 如果没有使用this,那么就是static方法,否则就是成员方法
*
*/