java -- 函數式編程

来源:https://www.cnblogs.com/paopaoT/archive/2023/04/20/17337998.html
-Advertisement-
Play Games

函數式編程 面向對象過分強調“必須通過對象的形式來做事情”,而函數式思想則儘量忽略面向對象的複雜語法——強調做什麼,而不是怎麼做。 有時只是為了做某事情而不得不創建一個對象,而傳遞一段代碼才是我們真正的目的。 Lambda Lambda是一個匿名函數,可以理解為一段可以傳遞的代碼。 當需要啟動一個線 ...


函數式編程

面向對象過分強調“必須通過對象的形式來做事情”,而函數式思想則儘量忽略面向對象的複雜語法——強調做什麼,而不是怎麼做
有時只是為了做某事情而不得不創建一個對象,而傳遞一段代碼才是我們真正的目的。

Lambda

Lambda是一個匿名函數,可以理解為一段可以傳遞的代碼。
當需要啟動一個線程去完成任務時, 通常會通過java.lang.Runnable介面來定義任務內容,並使用java.lang.Thread類來啟動該線程
傳統寫法,代碼如下:

public class Demo {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("多線程任務執行!");
            }
        }).start();
    }
}

藉助Java 8的全新語法,上述Runnable介面的匿名內部類寫法可以通過更簡單的Lambda表達式達到同樣的效果:

public class Demo04LambdaRunnable {
    public static void main(String[] args) {
        new Thread(() -> System.out.println("多線程任務執行!")).start(); // 啟動線程
    }
}

Lambda的優點 簡化匿名內部類的使用,語法更加簡單。

前提條件

必須是介面, 介面中有且只有一個抽象方法

有且僅有一個抽象方法的介面,稱為函數式介面

格式

Lambda表達式的標準格式為:

() -> {}
() 參數列表,無參數留空
-> 固定寫法, 代表指向動作
{} 方法體

省略規則
在Lambda標準格式的基礎上,使用省略寫法的規則為:

  1. 參數類型可省略
  2. 如果只有一個參數 ()可以省略
  3. 如果方法體只有一句話 return 大括弧 分號都可省略, 但必須同時省略
new Thread(() -> System.out.println("省略格式開啟線程")).start();

原理

  1. 在匿名方法所在類中新增一個方法,方法體為Lambda表達式中的代碼
  2. 運行時形成一個新的類,並實現對應介面
  3. 重寫方法的方法體中調用匿名方法所在類中新生成的方法.

函數式介面

函數式介面在Java中是指:有且僅有一個抽象方法的介面
函數式介面,即適用於函數式編程場景的介面。而Java中的函數式編程體現就是Lambda,所以函數式介面就是可以適用於Lambda使用的介面。

從應用層面來講,Java中的Lambda可以看做是匿名內部類的簡化格式,但是二者在原理上不同。

格式

只要確保介面中有且僅有一個抽象方法即可:

修飾符 interface 介面名稱 {
    public abstract 返回值類型 方法名稱(可選參數信息);
}

由於介面當中抽象方法的public abstract是可以省略的,所以定義一個函數式介面很簡單:

public interface MyFunctionalInterface {
    void myMethod();
}

@FunctionalInterface

@FunctionalInterface 該註解可用於一個介面的定義上:

@FunctionalInterface
public interface MyFunctionalInterface {
    void myMethod();
}

一旦使用該註解來定義介面,編譯器將會強制檢查該介面是否確實有且僅有一個抽象方法,否則將會報錯。不過,即使不使用該註解,只要滿足函數式介面的定義,這仍然是一個函數式介面

常用函數式介面

Supplier介面

java.util.function.Supplier<T>介面,它意味著"供給" , 對應的Lambda表達式需要“對外提供”一個符合泛型類型的對象數據。
抽象方法:
T get() 用來獲取一個泛型參數指定類型的對象數據
求數組元素最大值
使用Supplier介面作為方法參數類型,通過Lambda表達式求出int數組中的最大值

public class supplierInterface {
    public static void main(String[] args) {
        int[] arr = {3,24,346,4,13};
        method(() -> {
            Arrays.sort(arr);
            return arr[arr.length - 1];
        });
    }

    public static void method(Supplier<Integer> s) {
        Integer max = s.get();
        System.out.println(max);
    }
}
Consumer介面

java.util.function.Consumer<T> 介面不生產數據,而是消費一個數據,其數據類型由泛型參數決定
抽象方法
void accept(T t),意為消費一個指定泛型的數據
預設方法
default Consumer<T> andThen(Consumer<? super T> after)

public class _4_consumerInterface {
    public static void main(String[] args) {
        method("Hello World", (String s) -> {
            System.out.println(s.toUpperCase());
        });

        method("HEllO WorlD", s -> System.out.println(s.toLowerCase()));

        System.out.println("==========================");
        method("HEllO WorlD", (String s) -> {
            System.out.println(s.toUpperCase());
        }, (String s) -> {
            System.out.println(s.toLowerCase());
        });
        method("HEllO WorlD",
                s -> System.out.println(s.toUpperCase()),
                s -> System.out.println(s.toLowerCase())
        );
    }

    public static void method(String s, Consumer<String> c) {
        c.accept(s);
    }
    public static void method(String s, Consumer<String> c1, Consumer<String> c2) {
//        c1.accept(s);
//        c2.accept(s);
        // andThen c1.accept(s)後執行c2.accept(s) 等同於上面的寫法
        c1.andThen(c2).accept(s);
    }
}
Function介面

java.util.function.Function<T,R> 介面用來根據一個類型的數據得到另一個類型的數據,前者稱為前置條件,後者稱為後置條件
抽象方法:
R apply(T t) 根據類型T的參數獲取類型R的結果

public class Test {
    public static void main(String[] args) {
        Function<String,Integer> f = new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                return Integer.parseInt(s);
            }
        };

        Integer apply = f.apply("100");
        System.out.println(apply);

        Function<String,Integer> f2 = s -> Integer.parseInt(s);
        System.out.println(f2.apply("200"));
    }
}

預設方法:andThen

Function介面中有一個預設的andThen方法,用來進行組合操作,與Consumer介面相同

public class Test {
    public static void main(String[] args) {
        method5("10", (String s) -> {
            return Integer.parseInt(s);
        }, (Integer i) -> {
            return i * 10;
        });
        method5("100", s -> Integer.parseInt(s), i -> i * 10);
        method5("1000", Integer::parseInt, i -> i * 10);
    }
    private static void method5(String s, Function<String, Integer> f1, Function<Integer, Integer> f2) {
//        Integer i = f1.apply(s);
//        Integer n = f2.apply(i);
        Integer n = f1.andThen(f2).apply(s);
        System.out.println(n);
    }
}

Function的前置條件泛型和後置條件泛型可以相同

Predicate介面

java.util.function.Predicate 判斷型介面
抽象方法: boolean test(T t) 返回boolean

public class predicateInterface {
    public static void main(String[] args) {
        method("HelloWorld.java", (String s) -> {
            return s.toLowerCase().endsWith(".java");
        });

        method("Hello.java", s -> s.toLowerCase().endsWith(".java"));

    }
    public static void method(String filename, Predicate<String> p) {
        boolean b = p.test(filename);
        System.out.println(b);
    }
}

預設方法
Predicate<T> and(Predicate<? super T> other) 並且, 底層使用 &&
Predicate<T> or(Predicate<? super T> other) 或者, 底層使用 ||
Predicate<T> negate() 取反, 底層使用 !

public class Test {
    public static void main(String[] args) {
        method("Helloworld" ,s -> s.contains("H"), s -> s.contains("W"));
    }
    private static void method(String str ,Predicate<String> one, Predicate<String> two) {
        boolean b1 = one.test(str);
        boolean b2 = two.test(str);
        System.out.println("字元串符合要求嗎:" + (b1 && b2));

        boolean isValid = one.and(two).test(str);
        System.out.println("字元串符合要求嗎:" + isValid);
    }
}

public class Test {
    public static void main(String[] args) {
        method("Helloworld" ,s -> s.contains("H"), s -> s.contains("W"));
    }
    private static void method(String str ,Predicate<String> one, Predicate<String> two) {
        boolean b1 = one.test(str);
        boolean b2 = two.test(str);
        System.out.println("字元串符合要求嗎:" + (b1 || b2));

        boolean isValid = one.or(two).test(str);
        System.out.println("字元串符合要求嗎:" + isValid);
    }
}


public class Test {
    public static void main(String[] args) {
       isLong("aaa", new Predicate<String>() {
           @Override
           public boolean test(String s) {
               return  s.length()<5;
           }
       });
       isLong("bbbaa",s -> s.length()>=5);
    }
    public static void isLong(String s , Predicate<String> p){
        boolean test = p.test(s);
        System.out.println(!test);
        boolean b2 =  p.negate().test(s);
        System.out.println(b2);
    }
}

方法引用

前提

Lambda表達式中只有一句話時 可能使用

格式

符號表示 : ::
符號說明 : 雙冒號為方法引用運算符,而它所在的表達式被稱為方法引用
推導與省略 : ** 如果使用Lambda,那麼根據“可推導就是可省略**”的原則,無需指定參數類型,也無需指定的重載形式——它們都將被自動推導

應用Lambda表達式 , 在accept方法中接收字元串 , 目的就是為了列印顯示字元串 , 那麼通過Lambda來使用它的代碼很簡單:

public class DemoPrintSimple {
    private static void printString(Consumer<String> data, String str) {
        data.accept(str);
    }
    public static void main(String[] args) {
      	printString(s -> System.out.println(s), "Hello World");
    }
}

使用方法引用進行簡化

public class DemoPrintRef {
    private static void printString(Consumer<String> data, String str) {
        data.accept(str);
    }
    public static void main(String[] args) {
      	printString(System.out::println, "HelloWorld");
    }
}

其他引用

public class _5_functionInterface {
    public static void main(String[] args) {
        method("100", (String s) -> {
            return Integer.parseInt(s);
        });
        method("10", s -> Integer.parseInt(s));
        /*
            類名引用靜態方法
                類名::方法名
         */
        method("1000", Integer::parseInt);
        /*
            類名引用構造方法
                類名::new
         */
        method2("張三", Person::new);
        method2("李四", Person::new);
        method2("王五", s -> new Person(s));
        method3(Person::new);
         /*
            數組引用構造方法
                數據類型[]::new
         */
        method4(5,int[]::new);
        method4(3, (Integer i) -> {
            return new int[i];
        });
        method4(1, i -> new int[i]);

    }

    public static void method(String s, Function<String, Integer> f) {
        Integer n = f.apply(s);
        System.out.println(n);
    }
    private static void method2(String s, Function<String, Person> f) {
        Person p = f.apply(s);
        System.out.println(p);
    }
    private static void method3(Supplier<Person> su) {
        Person p = su.get();
        System.out.println(p);
    }
    private static void method4(Integer i, Function<Integer, int[]> f) {
        int[] arr = f.apply(i);
        System.out.println(Arrays.toString(arr));
    }
}
class Person {
    private String name;

    public Person() {}
    public Person(String name) {
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Rust 的面向對象編程特性 一、面向對象語言的特性 Rust是面向對象編程語言嗎? Rust 受到多種編程範式的影響,包括面向對象 面向對象通常包含以下特性:命名對象、封裝、繼承 對象包含數據和行為 “設計模式四人幫”在《設計模型》中給面向對象的定義: 面向對象的程式由對象組成 對象包裝了數據和操 ...
  • 最近看到一個有趣的問題:Person類具有Hand,Hand可以操作杯子Cup,但是在石器時代是沒有杯子的,這個問題用編程怎麼解決? 簡單代碼實現 我們先用簡單代碼實現原問題: @Data public class Person { private final String name; privat ...
  • 本文首發於公眾號:Hunter後端 原文鏈接:Django筆記二十六之資料庫函數之數學公式函數 這一篇來介紹一下公式函數,主要是數學公式。 其中 sin,cos 這種大多數情況下用不上的就不介紹了,主要介紹下麵幾種: Abs() 絕對值 Ceil() 向上取整 Floor() 向下取整 Mod() ...
  • 正所謂百家爭鳴、見仁見智、眾說紛紜、各有千秋!在工作流bpmn2.0可視化建模工具實現的細分領域,網上撲面而來的是 bpmn.js 這個渲染工具包和web建模器,而筆者卻認為使用flowable官方開源 editor-app 才是王道。 Flowable 開源版本中的 web 版流程設計器edito ...
  • 背景:1、正常啟動,xml文件放在java目錄和resource目錄下均正常 2、junit啟動,xml文件放在resource目錄下正常,放在java目錄下報BindingException錯誤 mapperlocation綁定地址為:"classpath:com/a/b/**/*.xml" 原因 ...
  • 禁止轉載 重寫了之前博客寫的泛型相關內容,全部整合到這一篇文章里了,把坑都填了,後續不再糾結這些問題了。本文深度總結了函數式思想、泛型對在Java中的應用,解答了許多比較難的問題。 純函數 協變 逆變 泛型通配符 PECS法則 自限定 Part 1: 協變與逆變 Java8 引入了函數式介面,從此方 ...
  • 教程簡介 PHP開發入門教程 - 一個簡單而簡短的PHP教程和所有內置PHP函數的完整參考手冊。本教程是為初學者和高級開發人員設計的。您將瞭解PHP內置函數,預定義變數示例,面向對象的PHP,數字,標量,數組,散列文件I / O,IF,ELSEIF,執行,迴圈,運算符,正則表達式,GET,POST, ...
  • 教程簡介 Hibernate是一個開放源代碼的對象關係映射框架,它對JDBC進行了非常輕量級的對象封裝,它將POJO與資料庫表建立映射關係,是一個全自動的orm框架,hibernate可以自動生成SQL語句,自動執行,使得Java程式員可以隨心所欲的使用對象編程思維來操縱資料庫。 Hibernate ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...