Spring Webflux
预备知识:
函数式编程,就是充分利用已经写好的函数,重点在于如何用这些函数实现功能
传统的命令式编程,就是要思考如何用一行行的代码实现功能
可以这样理解,传统的命令式编程就是在用代码实现函数功能,函数式编程是直接调用函数实现功能。
例子:命令式编程:通过for循环进行累加求和,这样就是去实现了求和的逻辑
函数式编程首先要了解lambda表达式,可以让代码更加简短。
Lambda表达式
实例: 匿名内部类省去了创建类的过程,而lambda表达式可以使这一过程更加简洁 public static void main(String args[]) { new Thread(){ @Override public void run() { System.out.pringln("OK"); } }.start(); 或 new Thread(new Runnable(){ @Override public void run() { System.out.println("ok"); } }).start(); } 使用lambda表达式: public static void main(String args[]) { new Thread(()-> System.out.println("ok")).start(); }
lambda本质是一个函数接口,用于快捷的创建一个接口实例并实现接口方法
表达式为: ()-> 方法 等价于 public class A implements B(){ @Override public void kk() { 。。。; } }
有几个注意事项:
①lambda仅适用于只有一个需要重写方法的接口,这种接口也可称为函数式接口,如果有多个方法需要重写,就不能明确lambda表达式所编写的方法是重写的哪一个方法,只有一个需要重写方法的概念用下面的例子解释
下例中,虽然接口A有两个方法,但是仅有一个write方法需要重写,read为default方法不需要重写,所以A接口仍然可以用lambda表达式实现。
public class test { public static void main(String[] args) { A a = ()-> System.out.println("write"); a.read(); a.write(); } } //对于函数式接口,可以加上@FunctionalInterface 加以标注,以表示其为一个函数式接口 @FunctionalInterface interface A { public void write(); default void read(){ System.out.println("read"); } }
②lambda表达式实际返回的是实现接口的实例,所以必须用函数接口接收lambda表达式返回值
上例中,如果使用 Object a = ()-> System.out.println("write"); 则会报错,因为Object并不是一个函数接口 可以强转类型解决 Object a = (A)()-> System.out.println("write"); 以实现Runnable接口为例: public class test { public static void main(String[] args) { Object r = (Runnable) ()-> System.out.println("run"); Thread t = new Thread((Runnable)r);//传入的r也需要转换为Runnable类型 t.start(); } }
下面介绍实现方法有参数的情况 该如何编写lambda表达式:
public class test { public static void main(String[] args) { //如果参数只有一个可以不写括号 A a = (i, str)-> System.out.println(i + str); a.write(10,"strstr"); //如果实现的方法体中有多行代码就需要加上{} A a = (i, str)-> { System.out.println(i + str); System.out.println("aa"); }; //方法有返回值,如果代码仅有一行 可以省略return,直接写返回值 B b1 = (i, str)-> i * 2; System.out.println(b.read(20, "read")); //方法有返回,且有多行 B b = (i, str)-> { System.out.println(str); return 2; }; b.read(2, "read"); } } interface A { public void write(int i,String str); } interface B { public int read(int i, String str); }
Jdk1.8的函数接口
jdk1.8提供了很多函数接口,不需要定义特定的接口,只需要创建出具备输入输出的函数就可以
public static void main(String[] args) { //创建了一个接口实例a,所实现的方法是输入一个Integer类型的参数 //输出为一个String类型的参数 Function<Integer, String> a = (i)-> "aaa" + i; //使用apply方法调用所实现的接口方法 String apply = a.apply(4); System.out.println(apply); } //
Function<T, R> 可以用于创建一个输入为T类型,输出为R类型的函数 Customer<T> 用于创建一个有输入没输出的函数,消费者函数接口 Supplier<T> 用于创建一个有输出没输入的函数,生产者函数接口 参照上表以此类推
方法引用
若Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用,即Lambda的另外一种表现形式。方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致。
① 对象引用::实例方法名 @Test public void testT1() { // 创建一个User类,属性为name和age User user = new User("LCY", 22); // Lambda表达式 Supplier<String> sup1 = () -> user.getName(); System.out.println(sup1.get()); // 方法引用方式 Supplier<Integer> sup2 = user::getAge; System.out.println(sup2.get()); } ②类名::静态方法名 @Test public void testT2() { Comparator<Integer> com1 = (x,y) -> Integer.compare(x, y); System.out.println(com1.compare(1, 2)); Comparator<Integer> com2 = Integer::compare; System.out.println(com2.compare(7, 6)); } ③类名::实例方法名 @Test public void testT3() { BiPredicate<String, String> bp1 = (x,y) -> x.equals(y); System.out.println(bp1.test("abcd", "dcba")); // 参数1是调用equals的,参数2是equals方法的参数 // 因此可以使用类名::方法名 BiPredicate<String, String> bp2 = String::equals; System.out.println(bp2.test("123", "123")); }
类型判断:
package lambda; @FunctionalInterface interface IMath { int add(int x, int y); } @FunctionalInterface interface IMath2 { int sub(int x, int y); } public class TypeDemo { public static void main(String[] args) { // 变量类型定义 IMath lambda = (x, y) -> x + y; // 数组里 IMath[] lambdas = { (x, y) -> x + y }; // 强转 Object lambda2 = (IMath) (x, y) -> x + y; // 通过返回类型 IMath createLambda = createLambda(); TypeDemo demo = new TypeDemo(); // 当有方法重写,存在二义性的时候,使用强转对应的接口解决 demo.test( (IMath2)(x, y) -> x + y); } public void test(IMath math) { } public void test(IMath2 math) { } public static IMath createLambda() { return (x, y) -> x + y; } }
Jdk8 新特性Stream流编程
概念:通过Stream流的方法实现更高级的特性
外部迭代 和 内部迭代
实现数组内数字的累加 外部迭代: int[] nums = {1, 2, 3}; int sum = 0; for (int num :nums) { sum += num; } 内部迭代: int sum = IntStream.of(nums).sum(); 通过流实现的方法更快捷的实现一些需求
中间操作/终止操作,惰性求值
int sum2 = IntStream.of(nums).map(i-> i * 2).sum(); System.out.println(sum2); map就是一个中间操作返回一个Stream操作, sum就是一个终止操作,返回一个结果 public static void main(String[] args) { int[] nums = {1, 4, 3}; IntStream intStream = IntStream.of(nums).map(i -> i * 2); System.out.println(intStream.sum()); } 返回流的是中间操作, 返回结果的是终止操作
使用Stream操作的步骤:
①创建流
public static void main(String[] args) { List<String> list = new ArrayList<>(); // 从集合创建 list.stream(); list.parallelStream();//并行流 // 从数组创建 Arrays.stream(new int[] { 2, 3, 5 }); // 创建数字流 IntStream.of(1, 2, 3); IntStream.rangeClosed(1, 10); // 使用random创建一个无限流 new Random().ints().limit(10); Random random = new Random(); // 自己产生流 Stream.generate(() -> random.nextInt()).limit(20); }
②中间操作:
③终止操作
分为短路和非短路操作
短路操作:需要将整个流执行完,返回一个最终结果
短路操作:在执行流的过程中找到符合条件的结果并返回