Java函数式编程(上)

embedded/2025/2/5 18:25:22/

函数

函数就是一种规则,给定输入就会有特定的输出,只要输入相同,无论多少次调用输入都是相同的。

如果函数引用了外部可变的数据那么这个函数就是不合格的函数(当外部引用数据改变时,在相同输入的情况下,输出可能会变化)。

有形的函数

将函数变为对象,函数的位置就是可变化的,哪里要用到这个函数的功能,就把它传递过去。

java">public static Sample{static int add(int a, int b){return a+b;}interface Lambda{int calculate(int a,int b);}Lambda add = (a,b)->a+b;public static void main(String[] args){Sample.add(1,2);add.calculate(1,2);}
}

函数对象的好处

行为参数化

java">//        这个函数对象的写法还是相当太保守了,其实非常的简单,甚至可以没有lambda接口的显示表示
//        判断逻辑就是一个行为,可以直接作为参数传递进方法中System.out.println(fileter(students,student -> student.getAge()>19));System.out.println(fileter(students,student -> student.getName().startsWith("洪")));interface Lambda{boolean test(Student student);
}/*** 通用的过滤方法* @param students* @param lambda 传递进来的函数对象* @return*/
static List<Student> fileter(List<Student> students,Lambda lambda){List<Student> res = new ArrayList<>();for (Student student : students) {
//            传入的Lambda其实是一个已经实现了Lambda接口的类的对象,
//            因此可以直接调用其中的方法,具体里面的方法的实现还是从main方法中传递进来的对象决定的if(lambda.test(student))res.add(student);}return res;
}

延迟执行

java">//不管是否满足debug执行的条件,等到执行到这条语句时会首先执行expensive()方法,但如果不符合条件就是非常的浪费
logger.debug("{}",expensive());
//传入一个函数对象,会先执行debug,通过其内部的具体实现判断是否要调用expensive()方法,更节约资源
logger.debug("{}",()->expensive());

函数编程语法

函数对象表现形式

Lambda表达式
java">(int a,int b)-> a+b;//只有一行语句不需要大括号也不需要return语句
(a,b)->a+b;//只有当通过上下文能够推断出参数类型的时候才能省略参数
(a,b)->{int c=a+b;return c;}interface Lambda{int op(int a,int b);
}
Lambda lambda = (a,b)->a+b;//能够通过上下文知道参数的类型a->a;//只有一个参数就可以省略小括号
方法引用
java">Math::max     (int a,int b)->Math.max(a,b);
System.out::println     (Object obj)->System.out.println(obj);
Student::getName       (Student stu)->stu.getName();
Student::new         ()->new Student();

函数对象类型

函数归类:参数个数和类型相同+返回类型相同------>同一个对象就能用一个函数式接口定义函数对象的类型

java">//函数对象
Type1 lambda1 = (int a)-> (a&1)==0;
Type1 lambda2 = (int a)-> BigInteger.valueOf(a).isProbablePrime(100);//在编译时检查是否满足函数式接口的条件
@FunctionalInterface
interface Type1{boolean test(int a);
}

使用泛型简化接口的编写:

java">@FunctionalInterface
interface Type3<T,I>{T op(I input);
}
Type3<Student,String > lambda5 = str ->new Student(str);
Type3<List<Student>,String > lambda6 = str -> List.of(new Student(str));
System.out.println(lambda5.op("your name"));
System.out.println(lambda6.op("your name"));

JDK自带的函数式接口:

java">Runnable: ()->void;//多线程编程中的任务对象
Callable: ()->T;//同上
Comparator: (T,T)->int;
Consumer,BiConsumer,IntConsumer(参数类型是int),LongConsumer,DoubleConsumer: (T)->void;
Function,BiFunction,IntFunction(参数类型是int)...... : (T)->R;
Predicate,BiPredicate(两个参数),IntPredicate(参数类型是int)......: (T)->boolean;
Supplier,IntSupplier(返回值是Int)...:()->T;
UnaryOperator(一个参数),BinaryOperator(两个参数),IntOperator(参数类型是int),...: (T)->T

方法引用

将现有方法的调用转化为函数对象

  • 静态方法:(String s)->Integer.parseInt(s)=====Integer::parseInt

    • 类名::静态方法

  • 非静态方法:(Student stu)-> stu.getName()===Student::getName

    • 需要多传入一个参数作为对象

  • 构造方法:()-> new Student()===Student::new

  • 对象::非静态方法(可以在类外使用)

    • this::非静态方法(只能在一个类里面使用)

    • super::非静态方法

  • 特例:对于无需返回值的函数接口(consumer、Runnable)它们可以配合有返回值的函数对象使用

java">	Consumer<Object> x = Sample2::print1;Function<Object,Integer> y = Sample2::print2;
//        有返回值的函数对象能够赋值给没有返回值的函数对象,只是没有办法接受到返回值了Consumer<Object> x1 = Sample2::print2;//会把结果忽略
static void print1(Object obj){System.out.println(obj);
}
static int print2(Object obj){System.out.println(obj);return 1;
}

闭包和柯里化

闭包Closure

函数对象与外界变量(静态变量、成员变量、方法参数变量)绑定到一起形成了一个整体,即一个闭包。

变量必须是final/effective final(没有被修改过的)

但是变量的内部状态是可以改变的(一个对象的内部变量发生变化)

java">int x = 10;//不能被修改
highOrder((int y)->x+y);//函数对象能够访问到这个x,x就和这个函数对象绑定到一起了
x++;//上面的会报错Student stu = new Student(19);
highOrder(y->y+stu.d);
//stu不能修改,但是其中的d属性是可以被修改的
stu.d = 20;
highOrder(y->y+stu.d);

违背了函数的不变性原则(多次使用同一个输入,输出结果必须相同)

闭包的作用:给函数执行提供数据的手段

java">//建立10个任务对象,并给每个任务对象一个任务编号
List<Runnable> list = new ArrayList<>();
for (int i = 0; i < 9; i++) {int k = i+1;//k的值在每次训循环中都不会改变,可以与任务对象组成闭包Runnable runnable = ()-> System.out.println("执行任务"+k);list.add(runnable);
}
list.forEach(Runnable::run);
柯里化Currying

将接受多个参数的函数转换成一系列接受一个参数的函数

(a,b)->a+b;==========>(a)->返回另一个函数对象==========>(b)->a+b;

java">F2 f2 = (a,b)->a+b;
//        柯里化:将两个参数的函数对象变成一个参数的函数对象
F1a fa = (a) -> (b) -> a+b;//定义最外层的函数对象
F1b fb = fa.op(10);//fa返回一个函数对象,携带了a数据,则fb与a形成了闭包
int r = fb.op(20);//fb最终返回计算结果,携带了b数据
System.out.println(r);

 柯里化的作用:让函数分步执行,所需要的数据不能一次性完成,需要分步收集

高阶函数

  • 指的是它是其他函数对象的使用者

  • 作用:

    • 将通用、复杂的逻辑隐含在高阶函数

    • 将易变、未定的逻辑放在外部的函数对象

  • 练习:高阶函数的内部循环、遍历二叉树、简单的Stream流

自定义Stream:

java">public class SimpleStream<T> {public static void main(String[] args) {List<Integer> list = List.of(1,2,3,4,5);SimpleStream.of(list).filter(x->(x&1)==1).map(x->x*x).forEach(System.out::println);}private final Collection<T> collection;public SimpleStream(Collection<T> collection){this.collection = collection;}
//    每一次返回的都是一个新的SimpleStream对象,为了防止对原始数据的修改public SimpleStream<T> filter(Predicate<T> predicate){List<T> result = new ArrayList<>();for (T t : collection) {if(predicate.test(t)){result.add(t);}}return new SimpleStream<>(result);}public <U> SimpleStream<U> map(Function<T,U> function){List<U> result = new ArrayList<>();for (T t : collection) {U u = function.apply(t);result.add(u);}return new SimpleStream<>(result);}public void forEach(Consumer<T> consumer){for (T t : collection) {consumer.accept(t);}}
//    相当于是一个工厂方法
//    静态泛型的定义需要在方法名之前再次声明一下public static <T> SimpleStream<T> of(Collection<T> collection){return new SimpleStream<>(collection);}//    按照某种规则将两两元素合并为一个public T reduce(T o, BinaryOperator<T> operator){T p = o;for (T t : collection) {p = operator.apply(p,t);}return p;}
}

 

 


http://www.ppmy.cn/embedded/144219.html

相关文章

kvm 改配cpu、内存、磁盘

kvm 改配 cpu virsh setvcpus vmxxx 12 --config 离线 virsh setvcpus vmxxx 12 --config --live 在线 kvm 添加内存 cat mem.xml <memory modeldimm> <target> <size unitGiB>1</size> <node>0</node> </target…

[创业之路-182]:《华为战略管理法-DSTE实战体系》-1-华为的发展历程和战略管理演变

目录 前言、华为在战略管理上做对了什么&#xff1f; 1、前瞻性的战略眼光 2、有效的战略解码 3、灵活的战略调整 4、注重创新和研发 5、以客户为中心的战略导向 6、完善的内部管理体系 一、华为不同时期的战略选择 1.1 华为不同时期的战略选择 1、创业初期&#xff…

base64转file文件对象

1.base64转file文件对象 //将base64转换为文件 dataURLtoFile(dataurl, filename) { var arr dataurl.split(,),mime arr[0].match(/:(.*?);/)[1],bstr atob(arr[1]),n bstr.length,u8arr new Uint8Array(n);while (n--) {u8arr[n] bstr.charCodeAt(n);}return new Fil…

保姆级教学 uniapp绘制二维码海报并保存至相册,真机正常展示图片二维码

一、获取二维码 uni.request({url: https://api.weixin.qq.com/wxa/getwxacode?access_token${getStorage("token")},responseType: "arraybuffer",method: "POST",data: {path: "/pages/index/index"},success(res) {// 转换为 Uint…

汽车48V电气系统

汽车48V电气系统 汽车48V电气系统汽车48V电气系统设计汽车48V电气系统测试汽车48V系统是48V供电和12V供电共存的么?48V供电系统是如何与12V供电系统共存的?48V电气系统测试的难点有哪些?在汽车48V电气系统通信测试中,如何向12V的控制器和48V的控制器供电?汽车48V电气系统通…

【服务器】Ubuntu、CentOS、Debian、Alibaba Cloud Linux等操作系统有什么不同?

Ubuntu 目标用户&#xff1a;Ubuntu 适合初学者和对图形界面友好的用户&#xff0c;也适用于开发人员和需要最新软件的企业。更新策略&#xff1a;Ubuntu 提供长期支持版本&#xff08;LTS&#xff09;&#xff0c;每两年发布一次&#xff0c;并提供五年的支持。非 LTS 版本每…

Python 中的迭代器:原理与使用

在 Python 中&#xff0c;迭代器是一个用于访问集合中元素的对象&#xff0c;常用于处理可迭代对象&#xff08;如列表、元组、字典、集合等&#xff09;。通过迭代器&#xff0c;我们可以方便地按顺序访问集合中的元素&#xff0c;而不需要显式地使用索引。 1. 迭代器的基础概…

大舍传媒-关于海外媒体宣发的探讨

关于海外媒体宣发的探讨 一、海外媒体宣发的重要性 在当今全球化的时代&#xff0c;海外媒体宣发对于企业、组织和个人来说具有至关重要的意义。通过有效的海外媒体宣发&#xff0c;可以提升品牌知名度&#xff0c;拓展国际市场&#xff0c;增强影响力&#xff0c;吸引更多的潜…