Java 函數(shù)式編程 總結(jié)

函數(shù)式編程

概念:

  • 使用代碼以某種方式操縱其他代碼,與傳統(tǒng)的面向過程的編程以及面向?qū)ο蟮木幊逃兴惶粯?。函?shù)式編程可以將方法作為參數(shù),并使得調(diào)用者動(dòng)態(tài)執(zhí)行行為。這很大地提高了代碼的可擴(kuò)展性和維護(hù)性。讓行為綁定更加靈活。

  • 其中大部分用到了Lambda表達(dá)式方法引用。有點(diǎn)類似與C++中的函數(shù)指針或是C#中的Delegate委托類??梢詫⒑瘮?shù)方法作為一個(gè)變量傳遞給某個(gè)通用形式的接收器。之后用這個(gè)接收器去執(zhí)行回調(diào)操作。

  • 關(guān)于回調(diào)函數(shù)的定義:回調(diào)函數(shù)就是一個(gè)通過函數(shù)指針調(diào)用的函數(shù)。如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個(gè)函數(shù),當(dāng)這個(gè)指針被用來調(diào)用其所指向的函數(shù)時(shí),我們就說這是回調(diào)函數(shù)?;卣{(diào)函數(shù)不是由該函數(shù)的實(shí)現(xiàn)方直接調(diào)用,而是在特定的事件或條件發(fā)生時(shí)由另外的一方調(diào)用的,用于對(duì)該事件或條件進(jìn)行響應(yīng)。

  • 函數(shù)指針是C++的概念,在Java中可以理解為函數(shù)的引用。

函數(shù)式接口

  • 語法規(guī)則: 有且僅有一個(gè)抽象方法的接口才能被視為函數(shù)式接口(Functional Interface)。

  • 通常用 @FunctionalInterface來進(jìn)行與其它接口進(jìn)行區(qū)分注釋。

@FunctionalInterface 
interface Callable{ void call();}
  • 用函數(shù)式接口定義的變量來接收右側(cè)的方法引用時(shí),右側(cè)的方法引用會(huì)隱式地轉(zhuǎn)化為函數(shù)式接口的變量。

? 函數(shù)式接口變量 = 方法的引用 ( = 兩側(cè)性質(zhì)其實(shí)不同,在Java的語法下會(huì)進(jìn)行隱式轉(zhuǎn)換)。

class Person{
    void eat(){ ... };
}

class Test{
    void test(){
        Person p = new Person();
        Callable c = p::eat;
        c.eat();    //調(diào)用時(shí),直接通過函數(shù)式接口中唯一的抽象方法來調(diào)用。
    }
}

Lambda表達(dá)式

又叫匿名函數(shù),Lambda表達(dá)式可以作為方法引用通過等號(hào)隱式轉(zhuǎn)化為左側(cè)的函數(shù)式接口變量。

interface Runnable{ void run();} //定義了一個(gè)只含一個(gè)函數(shù)的接口

class Test{
    void test{     
        Runnable r;  //用接口定義一個(gè)變量
        r = () -> System.out.println("...") ; // 將Lambda表達(dá)式作為函數(shù)引用 賦值給 r
        r.run(); //運(yùn)行
    }
}

Lambda表達(dá)式中會(huì)自行判斷參數(shù)以及返回值類型
例子略。

方法引用

interface Callable{ void call(int i)}

class Person{
    void walk(int step){...}
}

class Test{
    void test{
        Callbale c;
        c = new Person()::walk;  
    }
}

如果是 普通方法 的引用,就用 對(duì)象名::普通方法名。
如果是 靜態(tài)方法 的引用,就用 類名::靜態(tài)方法名
如果是 構(gòu)造方法 的引用,就用 類名::new。

如果是 動(dòng)態(tài)對(duì)象綁定 (原文是:非綁定接收)的引用, 還可以用 類名::普通方法名 的形式。這個(gè)時(shí)候需要在函數(shù)式接口的抽象方法上必須加一個(gè)參數(shù),作為調(diào)用者傳入。

class Person{
    void eat(){ System.out.println("I'm eating"); }
}

interface Activity{
    void doSomething(Person someone); //必須有個(gè)參數(shù),用來接收調(diào)用者
}

class Test{
    void test(){
        Person person = new Person();
        Activity act = Person::eat;   //這個(gè)Person::eat 實(shí)際上是一個(gè)Lambda表達(dá)式的簡易寫法: 
                                    //                 (someone) -> { someone.eat(); }
        act.doSomething(person); 
    }
}

用這種方式有個(gè)好處就是可以把調(diào)用者行為分開來,動(dòng)態(tài)調(diào)用的時(shí)候很方便。比如下面這個(gè)例子:

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

    void eat(){ System.out.println(name+" is eating"); }
    void sleep(){ System.out.println(name+" is sleeping"); }
    void walk(){ System.out.println(name+" is walking"); }
    void work(){ System.out.println(name+" is working");}
}

@FunctionalInterface
interface Activity{
    void doSomething(Person someone);
}

public class FPTest1 {
    public static void main(String[] args) {
        Person[] player = {
                new Person("Jimmy"),
                new Person("Fish"),
                new Person("Jack"),
                new Person("Rose")
        };

        Activity[] act = {
                Person::eat,
                Person::sleep,
                Person::walk,
                Person::work,
        };

        Scanner sc = new Scanner(System.in);
        int pi = sc.nextInt();
        int ai = sc.nextInt();

        SomeOneDoSomething( player[pi] , act[ai] ); //通過下標(biāo)索引,動(dòng)態(tài)改變"調(diào)用方"對(duì)應(yīng)要做的"行為"。
    }

    static void SomeOneDoSomething(Person someone, Activity activity){
        activity.doSomething(someone);  //將調(diào)用方作為參數(shù)傳入
    }
}



常用內(nèi)置接口

java.util.function包中有許多JDK提供的通用函數(shù)式接口。

其中主要為4種:

  • Suppiler : 用于無輸入地產(chǎn)生結(jié)果
  • Consumer : 用于對(duì)輸入進(jìn)行執(zhí)行或使用
  • Predicate : 用于謂語邏輯的判斷
  • Function : 用于對(duì)輸入加工處理后輸出

1. Suppiler接口

首先是 Suppiler 的源碼:
抽象方法: T get() 無參數(shù),返回T類型

@FunctionalInterface
public interface Supplier<T> {
    T get();   
}

應(yīng)用:

public class Test {
    void test(){
        Supplier<String> func0 = () -> "Hi!" + "Jimmy!";  //直接調(diào)用Supplier<T>
        System.out.println( func0.get() );
    }
}

2. Consumer接口

首先是 Consumer 的源碼:
抽象方法: accept(T t) 參數(shù)類型T,無返回類型

public interface Consumer<T> {
    void accept(T t);
    default Consumer<T> andThen(Consumer<? super T> after) {...} //還有這個(gè)默認(rèn)方法暫時(shí)這里不展開了
}

應(yīng)用:

public class Test {
    void test(){
        Consumer<String> func1 = s -> System.out.println(s);
        func1.accept("msg1");
    }
}

3. Predicate接口

首先是 Predicate 的源碼:
抽象方法: boolean test(T t) 接收T類型,返回boolean

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
    default Predicate<T> and(Predicate<? super T> other) {...}  //暫略
    default Predicate<T> negate() {...} //暫略
    // ... 還有好多暫略
}

應(yīng)用:

public class Test {
    void test(){
        Predicate<Integer> isZero = num -> num==0? true:false;
        System.out.println(isZero.test(1));
    }
}

4. Function接口

首先是 Function 的源碼:
抽象方法:R apply(T t); 接收T類型,返回R類型

public interface Function<T, R> {
    R apply(T t);
    //... 其余暫略
}

應(yīng)用:

public class Test {
    void test(){
        Function<String,Integer> str2int = s -> Integer.parseInt(s);
        int a = str2int.apply("10");
        sout(a);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容