自学内容网 自学内容网

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);
    }
}

函数对象的好处

行为参数化

//        这个函数对象的写法还是相当太保守了,其实非常的简单,甚至可以没有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;
}

延迟执行

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

函数编程语法

函数对象表现形式

Lambda表达式
(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;//只有一个参数就可以省略小括号
方法引用
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();

函数对象类型

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

//函数对象
Type1 lambda1 = (int a)-> (a&1)==0;
Type1 lambda2 = (int a)-> BigInteger.valueOf(a).isProbablePrime(100);

//在编译时检查是否满足函数式接口的条件
@FunctionalInterface
interface Type1{
    boolean test(int a);
}

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

@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自带的函数式接口:

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)它们可以配合有返回值的函数对象使用

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(没有被修改过的)

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

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);

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

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

//建立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;

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:

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;
   }
}

 

 


原文地址:https://blog.csdn.net/weixin_74118846/article/details/144330969

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!