千家信息网

Java8函数式编程方法是什么

发表于:2024-11-24 作者:千家信息网编辑
千家信息网最后更新 2024年11月24日,这篇"Java8函数式编程方法是什么"文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇"J
千家信息网最后更新 2024年11月24日Java8函数式编程方法是什么

这篇"Java8函数式编程方法是什么"文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇"Java8函数式编程方法是什么"文章吧。

    什么是函数式编程

    函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。 函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数! 函数式编程最早是数学家阿隆佐·邱奇研究的一套函数变换逻辑,又称Lambda Calculus(λ-Calculus),所以也经常把函数式编程称为Lambda计算。

    Java8内置了一些常用的方法接口FunctionalInterface

    这种接口只定义了一个抽象方法,并且用@FunctionalInterface注解标记,如Predicate,Consumer,Function,Supplier,Comparator等等,这些都属于java.util.function包中

    @FunctionalInterfacepublic interface Predicate {    boolean test(T t);}@FunctionalInterfacepublic interface Consumer {    void accept(T t);}// 省略不贴了

    他们的特点是定义了函数的入参以及返回值,当使用时传入满足函数接口定义的表达式,即可通过编译器检查,下面会介绍函数接口和对应的4种使用方式

    通过一个示例来看看使用函数式和不使用的区别,需求是要有一个函数,传入一个List,筛选出单数的项,另一个则筛选出双数的项,先看看不使用函数式的写法

        // 筛选出单数的方法    public static List filterSingular(List list) {        List result = new ArrayList<>();        for (Integer item : list) {            if (item % 2 != 0) {                result.add(item);            }        }        return result;    }    // 筛选出双数的方法    public static List filterEven(List list) {        List result = new ArrayList<>();        for (Integer item : list) {            if (item % 2 == 0) {                result.add(item);            }        }        return result;    }

    定义方法后调用,预期效果输出[1,3,5,7]和[2,4,5]

            List targetList = new ArrayList() {            {                this.add(1);                this.add(2);                this.add(3);                this.add(4);                this.add(5);                this.add(6);                this.add(7);            }        };        List singularList = filterSingular(targetList);        List evenList = filterEven(targetList);        System.out.println(singularList);        System.out.println(evenList);

    但其实这两个筛选函数,唯一区别只是判断条件的不同,这时候就可以将这个条件抽象成一个函数接口去编写,Predicate接口的test定义文章开头就有,传入一个泛型类型,返回一个boolean,改写下filter的代码

        public static List filter(List list,Predicate predicate) {        List result = new ArrayList<>();        for (Integer item : list) {            if (predicate.test(item)) {                result.add(item);            }        }        return result;    }

    将函数改造成了除了传入目前List外,还要传入一个实现了Predicate接口的实例对象,只需要传入满足函数定义入参和出参,就能通过编译,下面介绍4种这个函数的使用方式

    • 使用传统的匿名内部类,在java8之前只能这么操作

            List singularList = filter(targetList, new Predicate() {            @Override            public boolean test(Integer integer) {                return integer % 2 != 0;            }        });        System.out.println(singularList);
    • 使用lambda表达式格式如下()->{},()的是方法列表,->{}是方法体,由于目前只有一个参数,并且参数类型是可以推断出来的,所以类型和()可以不写,方法体只有一句,{}也可以不写,不推荐在方法体中写过长的代码,应保证可读性

            List singularList2 = filter(targetList, integer -> integer % 2 != 0);        // 下面是完整写法        // List singularList3 = filter(targetList, (Integer integer) -> {        //    return integer % 2 != 0;        // });

    可以使用的原因,lambda表达式满足传入Integer返回一个boolean的抽象操作,可以自动转化为函数接口

    • 静态方法引用,这里定义了一个静态方法,也可以自动的转化为函数接口,使用时需要用双冒号语法

        private static boolean integerWithSingular (Integer haha){        return haha % 2 != 0;    }

    使用静态方法引用,Cn是所在类名,这种方式对比lambda表达式可以让可读性进一步提高,因为方法有名字,可以通过名字去判断在执行什么操作,并且更适合编写更多的逻辑

        List singularList3 = filter(targetList, Cn::integerWithSingular);
    • 实例方法,因为任何实例方法,第一个参数永远都是一个隐藏的指针this指向当前实例,由于上面例子泛型传入的是Integer类型,需要改写下预期才能演示,先声明一个类,并且有一个实例方法是完成传入Test类型返回boolean的映射

    public class Test {    private long id;        public Test(long id) {        this.id = id;    }        private boolean integerWithSingular(){        return this.id % 2 != 0;    }}

    将filter函数的Integer类型全换成Test类型

        public static List filter(List list, Predicate predicate) {        List result = new ArrayList<>();        for (Test item : list) {            if (predicate.test(item)) {                result.add(item);            }        }        return result;    }

    下面的调用中,传入类名::实例方法名实现的效果是等价的

        ArrayList targetList = new ArrayList() {        {            this.add(new Test(1));            this.add(new Test(2));        }    };    filter(targetList,Test::integerWithSingular);

    任何只包含一个抽象方法的接口都可以被自动转换成函数接口,自己定义的接口没有标注@FunctionalInterface标注也可以

    用的比较多的函数接口

    • Consumer 输入一个对象,输出是空的,相当于消费掉传入的对象,ArrayList的forEach方法使用了Consumer

        // ArrayList的forEach方法源码    @Override    public void forEach(Consumer action) {        Objects.requireNonNull(action);        final int expectedModCount = modCount;        @SuppressWarnings("unchecked")        final E[] elementData = (E[]) this.elementData;        final int size = this.size;        for (int i=0; modCount == expectedModCount && i < size; i++) {            action.accept(elementData[i]);        }        if (modCount != expectedModCount) {            throw new ConcurrentModificationException();        }    }
    • Function更加接近于函数的定义,用于将一个类型变换成另一个类型,如数学中的函数把X变成Y,函数接口的定义如下,还是以刚才编写的Test类为理解,再编写一个map方法

        public static String map(Test test, Function function) {        return function.apply(test);    }

    只要满足传入一个Test类型,返回一个String类型的东西都可以被自动转换

            map(new Test(1),test -> "name");                // 如果Test类型还有一个属性为String的name和对应的getter方法,可以写成下面这种实例方法引用        // map(new Test(2), Test::getName);
    • Supplier和Consumer是对立者,Consumer消费,Supplier提供,从虚空中提供一个东西

        public static Object create(Supplier supplier){        return supplier.get();    }

    只要满足凭空冒出一个东西的条件即可

        create(Object::new);    // new的作用也是从虚无创造出一个对象,所以可以这么写    create(() -> "supplier");    create(() -> new Test(1));

    最后再介绍函数式编程在排序中的使用

        // Collections.sort的静态方法定义    public static  void sort(List list, Comparator c) {        list.sort(c);    }    // Comparator.comparing的静态方法定义    // 理解成需要传入一个T类型映射到U类型的形式即可    // 对应着示例就是传入一个Test,返回一个实现了Comparable接口的对象(如Integer,String...)    public static > Comparator comparing(            Function keyExtractor)    {        Objects.requireNonNull(keyExtractor);        return (Comparator & Serializable)            (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));    }

    下面是爽快时间

        // 使用简短的代码就能实现按对象中某个字段去排序    public static void main(String[] args) {        ArrayList tests = new ArrayList() {            {                this.add(new Test(2, "abc"));                this.add(new Test(1, "efg"));            }        };        // 现在Test实例的id字段排序,再将数组反转,然后再按照name字段排序        Collections.sort(tests, Comparator.comparing(Test::getId)                .reversed()                .thenComparing(Test::getName));        System.out.println(tests);    }

    以上就是关于"Java8函数式编程方法是什么"这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注行业资讯频道。

    0