侧边栏壁纸
博主头像
春潮带雨晚来急博主等级

行动起来,活在当下

  • 累计撰写 32 篇文章
  • 累计创建 1 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

lambda 1

Administrator
2023-12-26 / 0 评论 / 0 点赞 / 94 阅读 / 9074 字
> SpringBoot2.0不容错过的新特性 WebFlux响应式编程

---
#### 第一章 : 课程介绍

#### 第二章 : 函数式编程和Lambda表达式

###### 1.找出数组中最小的值

-   1 .命令式编程
```
public class TestController {
	private static final int LENGTH = 10;
	private static final int BOUND = 99;
	public static void main(String[] args) {
		int min = Integer.MAX_VALUE;
		int[] result = arrayListNum();
		System.out.println(Arrays.toString(result));
		for (int i : result) {
			if (i < min) {
				System.out.println(min);
				min = i;
			}
		}
		System.out.println("the min is :" + min);
	}
	public static int[] arrayListNum() {
		int[] ints = new int[LENGTH];
		for (int i = 0; i < LENGTH; i++) {
			ints[i] = new Random().nextInt(BOUND);
		}
		return ints;
	}
}
```
-   2.函数式编程
```
int asInt = IntStream.of(result).min().getAsInt();
System.out.println("最小值:" + asInt);

如果是下面这样:
int asInt = IntStream.of(result).parallel().min().getAsInt();

意思是:如果这个result数组有几亿条,关键字 .paraller() 会对这个数组进行拆分,这个流会并行执行,自己内部定义线程池和多线程和数据拆分
```

###### 2.创建多线程的方式

-   1.JDK8之前的
```
new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("ok");
			}
		}).start();
```
-   JDK8之后的 (用lambda表达式)
```
new Thread(() -> System.out.println("ok")).start();

// 此种方式 不用去管内部实现了什么接口,是哪种方式创建的
```

###### 3.关于接口的Lambda写法
```
public class LambdaDemo1 {
	public static void main(String[] args) {
		Interface1 i1 = i -> i * 2;
		/** 上下为两种常用写法; */
		Interface1 i2 = (i) -> i * 2;
		/** 可以指明类型 */
		Interface1 i3 = (int i) -> i * 3;
		Interface1 i4 = (int i) -> {
			System.out.println("只有一行的之后不用加花括号,并且默认是return返回的");
			return i * 4;
		};
	}
}

// @FunctionalInterface
interface Interface1 {
	int doubleNum(int i);
}
```
**备注:**
-   1 .如果要这样用Lambda的话,上面的接口里面 ==只允许有一个要实现的方法,这一个方法也称为“默认方法!”== ,(注意不是“默认实现方法”,默认实现方法叫做default .....)
-   2 .不加<code>@FunctionalInterface</code>这个注解也是可以运行的,但是还是建议加上。为了代码的完整性,这个注解也可检查下面的代码的写法是否正确。比如下面是否有多个方法,有此注解就会报错表示不能多个方法(特例下面4除外)
-   3 .这种设计原理是“单一职责模式”,即是将接口设计的越小越细越好!做到一个接口只干一件事情!接口可以多重继承,所以拆的很细只要多个继承了,使用效果也不会比原先一个接口里面有很多方法的那种差!
-   4 .JDK8之后,接口里面除了上面讲的==可以有一个“默认方法”之外,还有一个“默认实现方法”==,这个方法的存在,是不会让上面的讲的报错出现的,因为是内置的方法!
-   eg: ==default==,可以当做类中方法
```
public class LambdaDemo1 {
	public static void main(String[] args) {
		Interface1 i1 = i -> i * 2;
		Interface1 i2 = (i) -> i * 2;
		i2.add(3 ,5);
	}
}
@FunctionalInterface
interface Interface1 {
	int doubleNum(int i);
	default int add(int x, int y) {
		return x + y;
	}
}
```
-   ps:这种写法需要注意的是,在接口多重继承的时候可能会选择实现 继承的哪一个接口里面的默认方法
```
@FunctionalInterface
interface Interface1 {
	int doubleNum(int i);
	default int add(int x, int y) {
		return x + y;
	}
}
@FunctionalInterface
interface Interface2 {
	int doubleNum(int i);
	default int add(int x, int y) {
		return x + y;
	}
}
@FunctionalInterface
interface Interface3 extends Interface2, Interface1 {
	@Override
	default int add(int x, int y) {
		return 0;
	}
}
```
###### 4.函数接口
*特点*
-   1 .可以尽可能少的去定义接口
-   2 .可以实现链式编程

普通示例:
```
interface MoneyFormat {
	String format(int i);
}
class MyMoney {
	private int money;
	public MyMoney(int money) {
		this.money = money;
	}
	public void printMoney(MoneyFormat moneyFormat) {
		System.out.println("我的存款:" + moneyFormat.format(money));
	}
}
public class MoneyDemo {
	public static void main(String[] args) {
		MyMoney myMoney = new MyMoney(9999999);
		myMoney.printMoney(i -> new DecimalFormat("#,###").format(i));
	}
}
```
经过改造,并且加链式编程:
```
class MyMoney {
	private int money;
	public MyMoney(int money) {
		this.money = money;
	}
	public void printMoney(Function<Integer, String> moneyFormat) {
		System.out.println("我的存款:" + moneyFormat.apply(money));
	}
}
public class MoneyDemo {
	public static void main(String[] args) {
		MyMoney myMoney = new MyMoney(9999999);
		Function<Integer, String> moneyFormat = i -> new DecimalFormat("#,###").format(i);
		myMoney.printMoney(moneyFormat.andThen(s -> "人民币 " + s + "元"));
	}
}
```
-   结果:Lambda不关心是哪个接口,也不关系是实现了哪个方法。lambda只关系输入的是啥,输出的是啥。所以上面的上面的接口里面<code>String format(int i);</code>表示输入的是<code>int</code>,输出的是<code>String</code>
-   所以:改造的方法中就没有了接口,就只有了lambda中引入的只有输入输出的<code>Function</code><Integer, String>。这样就省掉了一个接口
-   并且:可以链式编程,此处就是链式编程加了一些前缀和后缀

###### 5. 接口:

接口 | 输入参数 | 返回类型 | 说明 
---|---|---|---
Predicate<T> | T | boolean | 断言 
Consumer<T> | T | / | 消费一个数据
Function<T,R> | T | R | 输入T输出R的函数 
Supplier<T> | / | T | 提供一个数据 
UnaryOperator<T> | T | T | 一元函数(输出输入类型相同) 
BiFunction<T,U,R> | (T,U) | R | 2个输入的函数 
BinaryOperator<T> | (T,T) | T | 二元函数(输出输入类型相同) 

-   eg:

```
public class LamTest {
	public static void main(String[] args) {
		  /** 断言函数接口 **/
		Predicate<Integer> predicate = i -> i > 0;
		System.out.println(predicate.test(10)); 
		===> true
		  /** 消费函数接口 **/
		Consumer<String> consumer = s -> System.out.println(s);
		consumer.accept("输入的数据");
		===> 输入的数据
	}
}
```
上面的尽量使用带有类型前缀的接口,比如原先的<code>Predicate<Integer></code>可以替换成<code>IntPredicate</code>,<code>IntConsumer</code>

###### 6. 方法引用
-   1.改造上面的“消费者接口”
```
原本为:
		Consumer<String> consumer = s -> System.out.println(s);
		consumer.accept("输入的数据");
```
```
改装为方法引用:
		Consumer<String> consumer = System.out::print;
		consumer.accept("接收到的数据");
```

作者的:
```
class Dog {
    private String name = "哮天犬";

    /** 默认10斤狗粮 */
    private int food = 10;

    public Dog() {

    }

    /** 带参数的构造函数
     * @param name
     */
    public Dog(String name) {
        this.name = name;
    }

    /** 狗叫,静态方法 */
    public static void bark(Dog dog) {
        System.out.println(dog + "叫了");
    }

    /** 吃狗粮 JDK <p>
     * 默认会把当前实例传入到非静态方法,参数名为this,位置是第一个;
     *
     * @param num
     * @return 还剩下多少斤
     */
    public int eat(int num) {
        System.out.println("吃了" + num + "斤狗粮");
        this.food -= num;
        return this.food;
    }

    @Override
    public String toString() {
        return this.name;
    }
}

public class MethodRefrenceDemo {

    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.eat(3);

        // 方法引用
        Consumer<String> consumer = System.out::println;
        consumer.accept("接受的数据");

        // 静态方法的方法引用
        Consumer<Dog> consumer2 = Dog::bark;
        consumer2.accept(dog);

        // 非静态方法,使用对象实例的方法引用
        // Function<Integer, Integer> function = dog::eat;
        // UnaryOperator<Integer> function = dog::eat;
        IntUnaryOperator function = dog::eat;

        // dog置空,不影响下面的函数执行,因为java 参数是传值
        dog = null;
        System.out.println("还剩下" + function.applyAsInt(2) + "斤");
        //
        // // 使用类名来方法引用
        // BiFunction<Dog, Integer, Integer> eatFunction = Dog::eat;
        // System.out.println("还剩下" + eatFunction.apply(dog, 2) + "斤");
        //
        // // 构造函数的方法引用
        // Supplier<Dog> supplier = Dog::new;
        // System.out.println("创建了新对象:" + supplier.get());
        //
        // // 带参数的构造函数的方法引用
        // Function<String, Dog> function2 = Dog::new;
        // System.out.println("创建了新对象:" + function2.apply("旺财"));

        // 测试java变量是传值还是穿引用
        List<String> list = new ArrayList<>();
        test(list);

        System.err.println(list);
    }

    private static void test(List<String> list) {
        list = null;
    }
}
```

###### 7. 类型判断
```
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;
		/** 通过返回类型1 */
		IMath createDlambda = createLambda();
		/** 通过返回类型2 */
		TypeDemo typeDemo = new TypeDemo();
		typeDemo.test(((x, y) -> x + y));
	}
	public static IMath createLambda() {
		return (x, y) -> x + y;
	}
	public void test(IMath iMath) { }
}
@FunctionalInterface
interface IMath {
	int add(int x, int y);
}
```

###### 8.变量引用
-   这个例子,类似于,<code>内部类</code>引用外部内的变量的时候,这个变量是不能再次赋值的。并且建议这个变量是用<code>final</code>来修饰。
-   原因:下面的第一个str是指向的"init_string",第二个str也是指向的这个堆对象。但是如果第一个str被重新赋值后,代码会报错,是因为第二个Str的地方根本不会引用这个新的赋值,而是依然会引用原本的"init_string"这个堆对象。

```
public class VarDemo {
    public static void main(String[] args) {
        String str = "init_string";
        // 建议 final String str = "init_string";
        // 不能再次赋值:   str = "新的内容";
        Consumer<String> consumer = s -> System.out.println(s + "   " + str);
        consumer.accept("消费这个字符串");
    }
}
```
###### 9. 级联表达式和柯里化
-   拆分: 箭头左边是输入(x),箭头右边是输出(y->x+y)  ; 右边的这个输入又是一个函数,即再次:  输入y , 输出 x+y
```
public class CurryDemo {
    public static void main(String[] args) {
        Function<Integer, Function<Integer, Integer>> fun = x -> y -> x + y;
        System.out.println(fun.apply(2).apply(3));
    }
}
```

#### 第三章 : Stream 流 编程

#### 第四章 :reactive stream 相应式流

#### 第五章 :webFlux服务端开发讲解

#### 第六章 :webFlux客户端声明式restClient框架开发讲解

0

评论区