1. 程式人生 > 其它 >函數語言程式設計介面

函數語言程式設計介面

技術標籤:javajava

函式式介面

概念
函式式介面在java指的是:有且僅有一個抽象方法的介面就稱為函式式介面。
函式式介面,使用於函數語言程式設計,在java當中的函數語言程式設計體現在Lambda,所以函式式介面就是用來服務Lambda表示式。只有確保介面當中有且僅有一個抽象方法,java中的lambda才能順利的進行推導。
備註:語法糖是指使用更加便利方便,但是原理不變的程式碼語法。就比如便利集合的時使用for-each語法,其實底層使用的是迭代器,這便是語法糖。
格式
只有確保介面當中有且僅有一個抽象方法即可:

修飾符 interface InterfaceName(){
//只能定義一個抽象方法 public abstract 返回值型別 方法名稱(引數列表)//還可以定義其他的非抽象方法 }

示例如下:

public interface FunctionInterfaceOne{
	public abstract void show01();
	public default void show02(){
	}
	//.............
	//void show03();有且僅有一個抽象方法,才被稱為函式式介面
}
@Functionallnterface註解

與@Overried註解作用型別,java8中專門為函式式介面引入一個新註解@functionalInterface,該註解主要定義在介面上。一旦在介面上使用該註解,編譯期將會強制檢查介面是不是一個函式式介面,該介面中是不是有且僅有一個抽象方法,如果不是,編譯報錯。

@FunctionalInterface
public interface FunctionInterfaceOne {
    //定義一個抽象方法
    void method();

    //void show();//有且僅有一個抽象方法
    public default void show(){

    }
}

自定義函式式介面的用途:
對於自定義的函式式介面,一般用於方法的引數和返回值上。

函數語言程式設計

能夠在兼顧java的面向物件特性基礎上,通過lambda表示式與後面的方法引用,為開發者開啟函數語言程式設計的大門。

Lambda的延遲載入

有些場景的程式碼執行執行後,結果不一定會被使用到,從未造成效能的浪費。為Lambda表示式是延遲執行的,正好可以解決此問題,提升效能。

程式碼如下:

 public static void main(String[] args) {
        // 定義一些日誌資訊
        String message1 = "執行mysqld.exe操作";
        String message2 = "執行java.exe操作";
        String message3 = "執行tomcat.exe操作";

        // 呼叫showLog方法,引數是一個函式式介面--BuildLogMessage介面,所以可以使用Lambda表示式
       /* showLog(2, () -> {
            // 返回一個拼接好的字串
            return message1 + message2 + message3;
        });*/

        // 簡化Lambda表示式
        /*
               使用Lambda表示式作為引數傳遞,
               只有滿足條件,日誌的等級小於等於3
                     才會呼叫此介面BuildLogMessage當中的方法buildLogMessage
                     才會進行字串的拼接動作
               如果條件不滿足,日誌的等級大於3
                      那麼BuildLogMessage介面當中的方法buildLogMessage也不會執行
                      所以拼接字串的動作也不會執行
                      所以不會存在效能上的浪費。
         */
        showLog(4, () -> {
            System.out.println("前面的日誌等級大於3,此處不執行!");
           return message1 + message2 + message3;
        });
  }

備註:實際上使用內部類也可以達到這樣的效果,只是將程式碼操作延遲到另外一個物件當中通過呼叫方法來完成。
後面的程式碼的執行取決於前面的條件的判斷結果。

使用Lambda作為方法的引數和返回值

在java當中,Lambda表示式是作為匿名內部類的替代品,如果一個方法的引數是一個函式式介面型別,那麼可以使用Lambda表示式進行替代
java.lang.Runnable介面就是一個函式式介面
程式碼如下:

public class Demo01Lambda{
	//定義一個方法,開啟執行緒的方法
	public static void startThread(Runnable r){
		new Thread(r).start();
	}
	public static void main(String[] args){
		startThread(()->{
			System.out.println("開啟一個新執行緒,執行緒任務被提升了!");
		});
		//優化Lambda
		startThread(()->System.out.println("開啟一個新執行緒,執行緒任務被提升了!"));
	}
}

如果一個方法的返回值型別是一個函式式介面,那麼我們可以直接使用一個Lambda表示式。
java.util.Comperator介面是一個函式式介面
程式碼如下:

public class Demo02Lambda{
	//定義一個方法,方法的返回值型別是一個函式式介面型別Comparator
	public static Comparator<String> createComparator(){
		//返回值就是一個函式式介面  內部類的方式
		/*return new Comparator(){
			@Overried
			public int compare(String o1,String o2){
				//自定義比較的規則  升序/降序
				//字串的長度
				return o1.length() - o2.length();//升序
			}
		}*/
		//使用Lambda 字串的長度升序
		return (o1,o2)-> o1.length() - o2.length();
	}
	public static void main(String[] args){
		String[] strs = {"aaa","a","abcdefg","ggggg"};
		Arrays.sort(strs,createComparator());
		System.out.println(Arrays.toString(strs));//{"a","aaa","ggggg","abcdefg"}
	}
}
常用的函式式介面

JDK提供了大量的常用函式式介面,豐富Lambda表示式的使用場景。他們主要在java.util.function包中被提供。
Supplier介面
java.util.function.Supplier <T> 介面,該介面有且僅有一個無參的方法:T get()。用來獲取一個泛型引數指定型別的物件資料。由於該介面是一個函式式介面,所以我們可以使用Lambda表示式來操作它。
Supplier<T>介面被稱之為生產型介面,指定介面的泛型是什麼型別,那麼介面中的get()方法就會生產什麼型別的資料。
程式碼如下:

  //定義一個方法,方法的引數傳遞一個Supplier<T>介面,泛型指定String,get方法就會返回一個String
    public static String getString(Supplier<String> sup){
        return sup.get();
    }
    //定義一個方法,方法的引數傳遞一個Supplier<T>介面,泛型指定為Integer型別,get方法就會返回一個int
    public static int getNum(Supplier<Integer> sup){
        return sup.get();
    }
    public static void main(String[] args) {
       /* //呼叫getString方法,方法的引數傳遞Supplier<T>是一個函式式介面,那麼我們就可以使用Lambda
        String str = getString(() -> {
            //生產一個字串並返回
            return new String("你好java");
        });
        System.out.println(str);*/
       //求一個int型別的陣列中的最值
        int[] arr = {10,20,5,8,3,30};
        int maxNum = getNum(() -> {
            //求出陣列的最大值
            int max = arr[0];
            for (int i : arr) {
                //判斷
                if (max < i) {
                    max = i;
                }
            }
            return max;
        });
        System.out.println(maxNum);
    }

Consumer介面
**java.util.function.Consumer<T>**介面剛好和Supplier介面相反,它不是用來生產一個數據的,而是用來消費一個數據。
資料的型別由泛型來指定。
accept方法
意思就是消費一個指定型別的資料。
程式碼如下:

 //定義一個方法,方法的引數傳遞一個Consumer<String>介面,傳遞一個字串變數
    public static void consumer(String str,Consumer<String> con){
        //使用消費型介面物件,消費傳遞的字串值
        con.accept(str);
    }
    public static void main(String[] args){
        //來呼叫消費方法consumer,Consumer<String>是一個函式式介面型別,所以可以使用Lambda表示式
        consumer("abcdefg", name ->{
            //把裡面的abcdefg字串改為大寫輸出
            String s = name.toUpperCase();
            System.out.println(new StringBuffer(s).reverse().toString());
        } );
    }

預設 的方法:andThen
如果一個方法的引數和返回值全都是Consumer型別的,那麼就可以實現這樣的效果:消費資料的時候,首先做一個消費的操作,再做一次消費的操作,實現組合。可以通過Consumer介面當中的預設方法:andThen來實現。
程式碼示例如下:

//定義一個方法,方法的引數傳遞一個字串和兩個consumer介面,Consumer介面的泛型指定為字串
    public static void consumers(String str, Consumer<String> con1,Consumer<String> con2){
        /*con1.accept(str);
        con2.accept(str);*/
        //andThen 連續消費 default Consumer<String> andThen
        //先執行左邊的consumer--con1動作,andThen---->再次執行Consumer--con2動作
        con1.andThen(con2).accept(str);
        //規則 con1連線con2,先執行con1消費資料,在執行con2消費資料
    }

    public static void main(String[] args) {
        //由於consumers方法的引數Consumer介面是一個函式式介面,可以使用Lambda表示式
        consumers("Java-中國最棒-都是業界大佬", name1->{
            //消費規則
            //擷取傳入的字串
            String substring = name1.substring(0, 6);
            System.out.println(substring);
        },name2-> {
            //定義消費規則 分成字串陣列進行展示
            String[] strs = name2.split("-");
            System.out.println(Arrays.toString(strs));//{"Java","中國最棒","都是業界大佬"}
        });
    }

通過檢視原始碼得知:endThen方法不允許傳入一個null物件否則就會丟擲一個控制針異常。
要想把兩次消費的動作連線起來,需要傳入兩個consumer介面,通過endThen方法實現一步一步執行消費動作。
例如一個小例子:

/*定義一個字串陣列,儲存一個人的資訊:"張三,20,鄭州市",,儲存5個人的資訊
    使用Consumer介面按照指定的格式進行一個列印輸出:姓名:張三;年齡:20;地址:鄭州市
    要求將列印姓名的動作作為第一個consumer介面的規則
    將列印年齡的動作作為第二個consumer介面的規則
    將列印地址的動作作為第三個consumer介面的規則*/
    //定義一個方法  方法的引數為一個Strng字串陣列    三個Consumer介面
    public static void consumer(String str[], Consumer<String> con1, Consumer<String> con2, Consumer<String> con3) {
        for (String s : str) {
            con1.andThen(con2).andThen(con3).accept(s);
            System.out.println();
        }
    }

    public static void main(String[] args) {
        String[] str = new String[5];
        str[0] = "張三,20,北京";
        str[1] = "李四,20,上海";
        str[2] = "王五,20,深圳";
        str[3] = "趙六,20,廣州";
        str[4] = "田七,20,蘇州";
        consumer(str, name->
                        System.out.print("姓名:" + name.split(",")[0] + ";"),
                age->
                        System.out.print("年齡:" + age.split(",")[1] + ";"),
                address->
                        System.out.print("地址:" + address.split(",")[2]));
    }