溫馨提示×

溫馨提示×

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

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

Java中如何實(shí)現(xiàn)函數(shù)式編程

發(fā)布時間:2021-06-18 14:12:33 來源:億速云 閱讀:222 作者:Leah 欄目:開發(fā)技術(shù)

這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)Java中如何實(shí)現(xiàn)函數(shù)式編程,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

    一、函數(shù)式概念

    函數(shù)式編程是一種結(jié)構(gòu)化編程的范式,主要思想是把運(yùn)算過程盡量寫成系列嵌套的函數(shù)調(diào)用。函數(shù)編程的概念表述帶有很抽象的感覺,可以基于案例看:

    public class Function01 {
        public static void main(String[] args) {
            // 運(yùn)算:(x+y)* c
            int x1 = 2 ;
            int y1 = 3 ;
            int c1 = 4 ;
            int sum1 = x1 + y1 ;
            int res1 = sum1 * c1 ;
            System.out.println("res1 = "+res1);
        }
    }

    這里基于過程的方式做計算,上面的代碼塊著重在描述程序執(zhí)行過程。

    在看基于函數(shù)的方式解決方法:

    public class Function02 {
        public static void main(String[] args) {
            // 函數(shù)式計算
            System.out.println("func01 = "+func01(2,3,4));
        }
        private static int func01 (int x,int y,int c){
            return (x+y)*c;
        }
    }

    函數(shù)式編程的核心要素:傳入?yún)?shù),執(zhí)行邏輯,返回值,也可以沒有返回值。

    函數(shù)式的編程風(fēng)格側(cè)重描述程序的執(zhí)行邏輯,不是執(zhí)行過程。

    同上面計算過程相比,函數(shù)式編程也減少很多臨時變量的創(chuàng)建,代碼風(fēng)格也變的簡潔清楚。

    二、函數(shù)與方法

    在Java語言中有函數(shù)式編程風(fēng)格,但是Java代碼中沒有函數(shù)的說法,而是稱為:方法;

    public class Function03 {
        public static void main(String[] args) {
            Func03 func03 = new Func03();
            func03.add(2);
            System.out.println(func03.res1);
        }
    }
    class Func03 {
        public int res1 = 0 ;
        public void add (int a1){
            this.res1 = a1 +1 ;
        }
    }

    類定義引用數(shù)據(jù)類型,類實(shí)例化后的對象可以調(diào)用類內(nèi)部的方法和數(shù)據(jù),這是最直觀的感覺。

    但是方法又有靜態(tài)和非靜態(tài)的區(qū)別,靜態(tài)方法屬于類所有,類實(shí)例化前即可使用。

    非靜態(tài)方法可以訪問類中的任何成員變量和方法,并且必須是類實(shí)例化后的對象才可以調(diào)用。

    三、JDK函數(shù)基礎(chǔ)

    1、Lambda表達(dá)式

    Lambda表達(dá)式也可稱為閉包,是推動Java8發(fā)布的最重要新特性,允許把函數(shù)作為一個方法的參數(shù)(函數(shù)作為參數(shù)傳遞進(jìn)方法中)。

    這里就很鮮明的對比Lambda表達(dá)式語法和傳統(tǒng)用法。

    public class Lambda01 {
        interface LambdaOpera {
            int operation(int a, int b);
        }
        public static void main(String[] args) {
            LambdaOpera lambdaOpera = new LambdaOpera(){
                @Override
                public int operation(int a, int b) {
                    return a * b ;
                }
            };
            System.out.println(lambdaOpera.operation(3,2));
            LambdaOpera lambdaOpera01 = (int a, int b) -> a + b;
            LambdaOpera lambdaOpera02 = (int a, int b) -> a - b;
            System.out.println(lambdaOpera01.operation(3,2));
            System.out.println(lambdaOpera02.operation(3,2));
        }
    }

    在看一個直觀的應(yīng)用案例,基于Lambda的方式創(chuàng)建線程,可以使代碼變的更加簡潔緊湊:

    public class Lambda02 {
        public static void main(String[] args) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 2; i++) {
                        System.out.println(i);
                    }
                }
            }).start();
            // 對比 Lambda 方式
            new Thread(() -> {
                for (int i = 0; i < 2; i++) {
                    System.out.println(i);
                }
            }).start();
        }
    }

    在看一下Runnable接口的結(jié)構(gòu):

    FunctionalInterface標(biāo)記在接口上,表示該接口是函數(shù)式接口,并且該接口只包含一個抽象方法,

    @FunctionalInterface
    public interface Runnable {
        public abstract void run();
    }

    Lambda表達(dá)式本身可以理解為就是一個接口的實(shí)現(xiàn)過程,這里runnable就是完整的Lambda表達(dá)式聲明:

    public class Lambda04 {
        public static void main(String[] args) {
            Runnable runnable = () -> {
                System.out.println("run one...");
            };
            Thread thread = new Thread(runnable);
            thread.start();
        }
    }

    Lambda表達(dá)式最直觀的作用就是使得代碼變得異常簡潔,并且可以作為參數(shù)傳遞。

    2、函數(shù)式接口

    Lambda表達(dá)式雖然有很多優(yōu)點(diǎn),但是使用的時候需要定義一些接口用來完成編碼,這樣又使得表達(dá)式又變得重量級,Java8自身已經(jīng)提供幾個常見的函數(shù)式接口。

    • Function:輸入一個參數(shù),返回一個結(jié)果;

    • Consumer:輸入一個參數(shù),不返回結(jié)果;

    • BiFunction:輸入兩個參數(shù),返回一個結(jié)果;

    • BiConsumer:輸入兩個參數(shù),不返回任何結(jié)果;

    public class Lambda05 {
        public static void main(String[] args) {
            Function<Integer, Integer> function01 = x -> x * 2;
            System.out.println(function01.apply(2));
            BiFunction<Integer, Integer, Integer> function02 = (x, y) -> x * y;
            System.out.println(function02.apply(2, 3));
    
            Consumer<String> consumer01 = msg -> System.out.println("msg:"+msg);
            consumer01.accept("hello");
    
            BiConsumer<String,Integer> consumer02 = (msg,i)
                    -> System.out.println(msg+":"+i);
            consumer02.accept("world",3);
        }
    }

    如果面對更復(fù)雜的業(yè)務(wù)需求,可以自定義函數(shù)式接口去解決。

    四、Optional類

    1、Null判斷

    Optional類是Java函數(shù)式編程的應(yīng)用,主要用來解決常見的空指針異常問題。

    在Java編程的開發(fā)中,很多地方都能常見空指針異常的拋出,如果想避免這個問題就要加入很多判斷:

    public class Optional01 {
        public static void main(String[] args) {
            User user = new User(1,"hello") ;
            if (user != null){
                if (user.getName() != null){
                    System.out.println(user.getName());
                }
            }
        }
    }

    為了確保程序不拋出空指針這種低級的錯誤,在程序中隨處可以null的判斷,代碼顯然冗余和繁雜。

    2、Optional應(yīng)用

    基于Optional類創(chuàng)建的對象可能包含空值和null值,也同樣會拋出對應(yīng)的異常:

    public class Optional02 {
        public static void main(String[] args) {
            // NoSuchElementException
            Optional<User> optionalUser = Optional.empty();
            optionalUser.get();
            // NullPointerException
            Optional<User> nullOpt = Optional.of(null);
            nullOpt.get();
        }
    }

    所以在不明確對象的具體情況下,使用ofNullable()方法:

    public class Optional03 {
        public static void main(String[] args) {
            User user = new User(1,"say");
            Optional<User> optionalUser = Optional.ofNullable(user);
            if (optionalUser.isPresent()){
                System.out.println(optionalUser.get().getName());
            }
            User user1 = null ;
            User createUser = Optional.ofNullable(user1).orElse(createUser());
            System.out.println(createUser.getName());
            User user2 = null ;
            Optional.ofNullable(user2).orElseThrow( ()
                    -> new RuntimeException());;
        }
        public static User createUser (){
            return new User(2,"hello") ;
        }
    }

    這樣看下來Optional結(jié)合鏈?zhǔn)椒椒ê蚅ambda表達(dá)式就很大程度上簡化了應(yīng)用的代碼量:

    public class Optional04 {
        public static void main(String[] args) {
            // 1、map轉(zhuǎn)換方法
            User user = new User(99, "Java");
            // user = null ;
            String name = Optional.ofNullable(user)
                    .map(u -> u.getName()).orElse("c++");
            System.out.println(name);
            // 2、過濾方法
            Optional<User> optUser01 = Optional.ofNullable(user)
                    .filter(u -> u.getName() != null && u.getName().contains("c++"));
            // NoSuchElementException
            System.out.println(optUser01.get().getName());
        }
    }

    Optional提供null處理的各種方法,可以簡潔很多代碼判斷,但是在使用風(fēng)格上和之前變化很大。

    五、Stream流

    如果Optional簡化很多Null的判斷,那Stream流的API則簡化了很多集合的遍歷判斷,同樣也是基于函數(shù)式編程。

    Java中如何實(shí)現(xiàn)函數(shù)式編程

    上述為Stream接口繼承關(guān)系如圖,同樣提供一些特定接口和較大的包裝接口,通過源碼查看,可以看到和函數(shù)編程也是密切相關(guān)。

    public class Stream01 {
        public static void main(String[] args) {
            Stream<String> stream = Stream.of("hello", "java");
            stream.forEach(str -> System.out.print(str+";"));
        }
    }

    Stream與函數(shù)接口結(jié)合使用,函數(shù)接口又可以使用Lambda表達(dá)式進(jìn)行簡化代碼。在Java8通過Stream可以大量簡化集合使用的代碼復(fù)雜度。

    public class Stream02 {
        public static void main(String[] args) {
            // 1、轉(zhuǎn)換Stream
            List<String> list = Arrays.asList("java+;", "c++;", "net;");
            list.stream();
            // 2、forEach操作
            list.stream().forEach(System.out::print);
            // 3、map映射,輸出 3,4
            IntStream.rangeClosed(2,3).map(x->x+1).forEach(System.out::println);
            // 4、filter過濾
            list.stream().filter(str -> str.contains("+")).forEach(System.out::print);
            // 5、distinct去重
            Integer[] arr = new Integer[]{3, 1, 3, 1, 2,4};
            Stream.of(arr).distinct().forEach(System.out::println);
            // 6、sorted排序
            Stream.of(arr).sorted().forEach(System.out::println);
            // 7、collect轉(zhuǎn)換
            List<String> newList = list.stream().filter(str -> str.contains("+"))
                    .collect(Collectors.toList());
            newList.stream().forEach(System.out::print);
        }
    }

    在沒有Stream相關(guān)API之前,對于集合的操作和遍歷都會產(chǎn)生大量的代碼,通過Stream相關(guān)API集合的函數(shù)式編程和Lambda表達(dá)式的風(fēng)格,簡化集合很多操作。

    上述就是小編為大家分享的Java中如何實(shí)現(xiàn)函數(shù)式編程了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細(xì)節(jié)

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

    AI