溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Java8函數(shù)式編程方法是什么

發(fā)布時間:2022-01-18 16:38:07 來源:億速云 閱讀:138 作者:iii 欄目:開發(fā)技術(shù)

這篇“Java8函數(shù)式編程方法是什么”文章的知識點大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Java8函數(shù)式編程方法是什么”文章吧。

    什么是函數(shù)式編程

    函數(shù)式編程就是一種抽象程度很高的編程范式,純粹的函數(shù)式編程語言編寫的函數(shù)沒有變量,因此,任意一個函數(shù),只要輸入是確定的,輸出就是確定的,這種純函數(shù)我們稱之為沒有副作用。而允許使用變量的程序設(shè)計語言,由于函數(shù)內(nèi)部的變量狀態(tài)不確定,同樣的輸入,可能得到不同的輸出,因此,這種函數(shù)是有副作用的。 函數(shù)式編程的一個特點就是,允許把函數(shù)本身作為參數(shù)傳入另一個函數(shù),還允許返回一個函數(shù)! 函數(shù)式編程最早是數(shù)學家阿隆佐·邱奇研究的一套函數(shù)變換邏輯,又稱Lambda Calculus(λ-Calculus),所以也經(jīng)常把函數(shù)式編程稱為Lambda計算。

    Java8內(nèi)置了一些常用的方法接口FunctionalInterface

    這種接口只定義了一個抽象方法,并且用@FunctionalInterface注解標記,如Predicate,Consumer,Function,Supplier,Comparator等等,這些都屬于java.util.function包中

    @FunctionalInterface
    public interface Predicate<T> {
        boolean test(T t);
    }
    
    @FunctionalInterface
    public interface Consumer<T> {
        void accept(T t);
    }
    // 省略不貼了

    他們的特點是定義了函數(shù)的入?yún)⒁约胺祷刂担斒褂脮r傳入滿足函數(shù)接口定義的表達式,即可通過編譯器檢查,下面會介紹函數(shù)接口和對應(yīng)的4種使用方式

    通過一個示例來看看使用函數(shù)式和不使用的區(qū)別,需求是要有一個函數(shù),傳入一個List<Integer>,篩選出單數(shù)的項,另一個則篩選出雙數(shù)的項,先看看不使用函數(shù)式的寫法

        // 篩選出單數(shù)的方法
        public static List<Integer> filterSingular(List<Integer> list) {
            List<Integer> result = new ArrayList<>();
            for (Integer item : list) {
                if (item % 2 != 0) {
                    result.add(item);
                }
            }
            return result;
        }
    
    
        // 篩選出雙數(shù)的方法
        public static List<Integer> filterEven(List<Integer> list) {
            List<Integer> result = new ArrayList<>();
            for (Integer item : list) {
                if (item % 2 == 0) {
                    result.add(item);
                }
            }
            return result;
        }

    定義方法后調(diào)用,預(yù)期效果輸出[1,3,5,7]和[2,4,5]

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

    但其實這兩個篩選函數(shù),唯一區(qū)別只是判斷條件的不同,這時候就可以將這個條件抽象成一個函數(shù)接口去編寫,Predicate接口的test定義文章開頭就有,傳入一個泛型類型,返回一個boolean,改寫下filter的代碼

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

    將函數(shù)改造成了除了傳入目前List外,還要傳入一個實現(xiàn)了Predicate接口的實例對象,只需要傳入滿足函數(shù)定義入?yún)⒑统鰠?,就能通過編譯,下面介紹4種這個函數(shù)的使用方式

    • 使用傳統(tǒng)的匿名內(nèi)部類,在java8之前只能這么操作

            List<Integer> singularList = filter(targetList, new Predicate<Integer>() {
                @Override
                public boolean test(Integer integer) {
                    return integer % 2 != 0;
                }
            });
            System.out.println(singularList);
    • 使用lambda表達式格式如下()->{},()的是方法列表,->{}是方法體,由于目前只有一個參數(shù),并且參數(shù)類型是可以推斷出來的,所以類型和()可以不寫,方法體只有一句,{}也可以不寫,不推薦在方法體中寫過長的代碼,應(yīng)保證可讀性

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

    可以使用的原因,lambda表達式滿足傳入Integer返回一個boolean的抽象操作,可以自動轉(zhuǎn)化為函數(shù)接口

    • 靜態(tài)方法引用,這里定義了一個靜態(tài)方法,也可以自動的轉(zhuǎn)化為函數(shù)接口,使用時需要用雙冒號語法

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

    使用靜態(tài)方法引用,Cn是所在類名,這種方式對比lambda表達式可以讓可讀性進一步提高,因為方法有名字,可以通過名字去判斷在執(zhí)行什么操作,并且更適合編寫更多的邏輯

        List<Integer> singularList3 = filter(targetList, Cn::integerWithSingular);
    • 實例方法,因為任何實例方法,第一個參數(shù)永遠都是一個隱藏的指針this指向當前實例,由于上面例子泛型傳入的是Integer類型,需要改寫下預(yù)期才能演示,先聲明一個類,并且有一個實例方法是完成傳入Test類型返回boolean的映射

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

    將filter函數(shù)的Integer類型全換成Test類型

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

    下面的調(diào)用中,傳入類名::實例方法名實現(xiàn)的效果是等價的

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

    任何只包含一個抽象方法的接口都可以被自動轉(zhuǎn)換成函數(shù)接口,自己定義的接口沒有標注@FunctionalInterface標注也可以

    用的比較多的函數(shù)接口

    • Consumer 輸入一個對象,輸出是空的,相當于消費掉傳入的對象,ArrayList的forEach方法使用了Consumer

        // ArrayList的forEach方法源碼
        @Override
        public void forEach(Consumer<? super E> 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更加接近于函數(shù)的定義,用于將一個類型變換成另一個類型,如數(shù)學中的函數(shù)把X變成Y,函數(shù)接口的定義如下,還是以剛才編寫的Test類為理解,再編寫一個map方法

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

    只要滿足傳入一個Test類型,返回一個String類型的東西都可以被自動轉(zhuǎn)換

            map(new Test(1),test -> "name");
            
            // 如果Test類型還有一個屬性為String的name和對應(yīng)的getter方法,可以寫成下面這種實例方法引用
            // map(new Test(2), Test::getName);
    • Supplier和Consumer是對立者,Consumer消費,Supplier提供,從虛空中提供一個東西

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

    只要滿足憑空冒出一個東西的條件即可

        create(Object::new);
        // new的作用也是從虛無創(chuàng)造出一個對象,所以可以這么寫
        create(() -> "supplier");
        create(() -> new Test(1));

    最后再介紹函數(shù)式編程在排序中的使用

        // Collections.sort的靜態(tài)方法定義
        public static <T> void sort(List<T> list, Comparator<? super T> c) {
            list.sort(c);
        }
    
        // Comparator.comparing的靜態(tài)方法定義
        // 理解成需要傳入一個T類型映射到U類型的形式即可
        // 對應(yīng)著示例就是傳入一個Test,返回一個實現(xiàn)了Comparable接口的對象(如Integer,String...)
        public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
                Function<? super T, ? extends U> keyExtractor)
        {
            Objects.requireNonNull(keyExtractor);
            return (Comparator<T> & Serializable)
                (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
        }

    下面是爽快時間

        // 使用簡短的代碼就能實現(xiàn)按對象中某個字段去排序
        public static void main(String[] args) {
            ArrayList<Test> tests = new ArrayList<Test>() {
                {
                    this.add(new Test(2, "abc"));
                    this.add(new Test(1, "efg"));
                }
            };
            // 現(xiàn)在Test實例的id字段排序,再將數(shù)組反轉(zhuǎn),然后再按照name字段排序
            Collections.sort(tests, Comparator.comparing(Test::getId)
                    .reversed()
                    .thenComparing(Test::getName));
            System.out.println(tests);
        }

    以上就是關(guān)于“Java8函數(shù)式編程方法是什么”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對大家有幫助,若想了解更多相關(guān)的知識內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細節(jié)

    免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI