java中的函數式接口


是什么??

有且只有一個抽象方法的接口

場景:

適用於函數式編程場景(使用lambda表達式編程)的接口,函數式接口可以適用於lambda使用的接口。

只有確保接口中有且只有一個抽象方法,java中的lambda才能順利推到

 

格式

/**
 * 函數式接口:有且之喲一個抽象方法的接口
 *          接口中可以包含其他的方法,包括默認方法,靜態方法,私有方法
 *
 * @FunctionalInterface
 * 作用:可以檢測接口是否是一個函數式接口
 *      是:編譯成功
 *      否:編譯失敗(接口中沒有抽象方法,抽象方法的個數大於1)
 */

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

//    void  method2();
}

 

函數式接口的使用:

/**
 * 函數式接口的使用:可以作為方法的參數和返回值類型
 */

Lambda作為參數

例如:

public class Demo {
    public static  void show(MyInterface myInterface){
        myInterface.method();
    }

    public static void main(String[] args) {
        //調用show方法,方法的參數是一個接口,所以可以傳遞接口的實現類對象
        show(new MyInterfaceImp());

        //調用show方法,方法的參數是一個接口,所以可以傳遞接口的匿名內部類
        show(new MyInterface() {
            @Override
            public void method() {
                System.out.println("使用匿名內部類重寫接口的抽象方法");
            }
        });

        //調用show方法,方法的參數是一個函數式接口,所以,我們可以使用lambda表達式
        show(()->{
            System.out.println("使用lambda表達式重寫接口中的抽象方法");
        });

        //簡化lambda表達式
        show(()-> System.out.println("簡化lambda表達式"));
    }
注意:使用匿名內部類回生成.class文件,但是lambda表達式不會,減少JVM的加載

結果:

 

 

 

函數式編程

 

Lambda的延遲執行減少性能浪費:

 

例子:

/**
 * 日志案例
 * 發現下面代碼存在性能浪費
 *
 * 調用showLog方法的過程:
 *      先將msg1 msg2 msg3拼接好之后,調用showLog方法。
 *      但是如果level不等於1,那就不執行了。
 *      所以拼接字符串的過程就浪費了
 */

 

public class Demo01Logger {
    public static void showLog(int level,String msg){
        if (level == 1){
            System.out.println(msg);
        }
    }

    public static void main(String[] args) {
        String msg1 = "AAA";
        String msg2 = "BBB";
        String msg3 = "CCC";

        showLog(1,msg1+msg2+msg3);
    }
}

 

 

利用lambda表達式優化:

1先定義一個函數式接口

@FunctionalInterface
public interface MessageBuilder {
    //定義拼接消息的抽象方法,返回拼接完成的消息
    public abstract String builderMsg();
}

 

/**
 * lambda優化
 *
 * lambda延遲加載:
 * lambda使用前提:必須存在函數式接口:
 */
public class Demo02Logger {
    //顯示日志方法,
    public static void showLog(int level,MessageBuilder messageBuilder){
        if (level == 1){
            System.out.println(messageBuilder.builderMsg());
        }
    }

    public static void main(String[] args) {
        String msg1 = "AAA";
        String msg2 = "BBB";
        String msg3 = "CCC";
        //調用showLog方法,參數MessageBuilder式一個函數式接口,可以傳遞Lambda表達式
        showLog(1,()->{
            //返回一個拼接好的字符串
            System.out.println("有沒有拼接");
            return msg1+msg2+msg3;
        });
    }
}

可以通過改變日志級別,看看輸出存不存在"有沒有拼接"

/**
 *使用lambda表達式作為參數傳遞,僅僅式吧參數傳遞到showLog方法中
 * 只有滿足條件,日志的等級式1
 *      才會調用接口MessageBuilder中的方法builderMessage
 *      進行字符串拼接
 * 如果不滿足
 *      接口MessageBuilder中的方法builderMessage不會執行
 *      不會導致性能浪費
 */

 

 

Lambda作為參數和返回值

 

public class Demo03Runable {
    //定義一個方法startThread 方法參數使用函數式接口Runnable
    public static void startThread(Runnable runnable){
        new Thread(runnable).start();
    }

    public static void main(String[] args) {
        //匿名內部類實現
        startThread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"線程啟動了");
            }
        });

        //因為Runnable接口是一個函數式接口,里面只有一個抽象方法run方法,可以使用lambda表達式
        startThread(()->{
            System.out.println(Thread.currentThread().getName()+"lambda線程啟動了");
        });

        startThread(()->
            System.out.println(Thread.currentThread().getName()+"優化lambda線程啟動了"));
        }

}

 

 

 

Lambda作為返回值

public class Demo02Comparator {
    //定義一個方法,方法的返回值位函數式接口Comparator
    public static Comparator<String> getComparator(){
        //方法返回值式一個接口,返回接口的匿名內部類
        return new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                //安裝字符串降序排序
                return o1.length() - o2.length();

            }
        };

    }

    public static Comparator<String> getComparator2() {
        //方法返回值式一個函數式接口,返回接口的lambda表達式
        return (String o1, String o2)->{
            return o1.length() - o2.length();
        };
    }

    //優化表達式
    public static Comparator<String> getComparator3() {
        //方法返回值式一個函數式接口,返回接口的lambda表達式
        return ( o1,  o2)-> o1.length() - o2.length();
    }

    public static void main(String[] args) {
        String[] arr = {"AAAAAAA","BBBB","CCCCCCC"};
        System.out.println(Arrays.toString(arr));
        //對數組進行拍尋
        Arrays.sort(arr,getComparator());
        System.out.println(Arrays.toString(arr));
    }

}

 

常用的函數式接口:

Supplier接口:

java.util.function.Supplier<T> 接口包含一個無參方法:T get() 

作用:用來獲取一個泛型參數指定類型的對象數據。

對應的lambda表達式需要提供一個符合泛型類型的對象數據

/**
 * 常用函數式接口
 *  java.util.function.Supplier<T> 接口被稱為生產型接口。
 * Supplier指定什么類型的泛型就返回什么類型的數據
 */

 

public class Demo01Supplier {
    //方法參數傳遞Supplier接口,泛型String,get返回一個String
    public static String getString(Supplier<String> supplier){
        return supplier.get();
    }

    public static void main(String[] args) {
        //調用getString方法。使用lambda表達大師
        String s = getString(()->{
            return "生成字符串";
        });

        //優化表達式
        String s1 = getString(()->"優化生成字符串");

        System.out.println(s);
        System.out.println(s1);
    }
}

結果:

生成字符串
優化生成字符串

 

簡單練習

/**
 * 求數組最大值
 *      使用Supplier接口作為參數類型,通過lambda表達式求出int數組的最大值
 */

答案:

public class Demo02Test {
    //獲取數組元素的最大值,參數位supplier接口,泛型Integer
    public static Integer getMax(Supplier<Integer> supplier){
        return supplier.get();
    }

    public static void main(String[] args) {
        //定義數組
        int[] arrint = {111,222,444,333};
        int maxinarr =getMax(()->{
           int max = arrint[0];
            for (int i : arrint) {
                if(i>max){
                    max = i;
                }
            }
            return max;
        });

        System.out.println(maxinarr);
    }

}

 

 

Consumer

/**
 * java.util.function.Consumer<T> 消費一個數據,數據類型又泛型決定
 * 消費型接口,使用型接口
 * accept
 *
 */

 

public class Demo03Consumer {
    public static void method(String name, Consumer<String> consumer){
        consumer.accept(name);
    }

    public static void main(String[] args) {
        //因為accept有參數,所以lambda表達式需要加入參數。
        method("quanzhiqiang",(String name)->{
            System.out.println(name);

            String reName = new StringBuffer(name).reverse().toString();
            System.out.println(reName);
        });


    }
}

結果:

quanzhiqiang
gnaiqihznauq

 

Consumer中的默認方法 andThen

/**
 * Consumer接口默認方法andThen
 * 作用:需要兩個Consumer接口,可以把兩個Consumer接口組合到一起,
 * 在對數據進行消費
 *
 * Consumer<String> co1
 * Consumer<String> co2
 * String str1 = "done"
 * co1.accept(str1)
 * co2.accept(str1)
 * 可以簡潔點:
 * co1.andThen(con2).accept(s)
 *
 * 連接兩個Consumer接口,再進行消費,誰寫前面誰先消費
 */

 

public class Demo4Consumer {
    public static void method(String str , Consumer<String> co1,Consumer<String> co2){
        co1.accept(str);
        co2.accept(str);
    }

    public static void method1(String str , Consumer<String> co1,Consumer<String> co2){
      co1.andThen(co2).accept(str);//執行co1再執行co2,比上面的要簡潔一些
    }

    public static void main(String[] args) {
        method1("quan",
                (t)->{//lambda表達式參數的類型,可以省略
                    System.out.println(t.toUpperCase());
                },
                (t)->{
                    System.out.println(t.toLowerCase());
                });


    }

}
/**
 * QUAN
 * quan
 * 結果
 */

 

練習:

/**
 * 字符串數組中存在多條信息,按照格式姓名:xx 性別:Xx格式打印出來
 * 答應名名字位第一個Consumer接口的lambda實例
 * 打印性別式第二個
 * 然后拼接起來
 */

public class Demo5Consmer {

    public static void pringinfo(String[] arr , Consumer<String> c1,Consumer<String> c2){
        for (String s : arr) {
           c1.andThen(c2).accept(s);
        }
    }
    public static void main(String[] args) {
        String[] arr = {"AA,N","BB,M","CC,N"};
        pringinfo(arr,
                ( msg)->{
//            對字符串進行分割,取出姓名
            String name = msg.split(",")[0];
                    System.out.print("姓名:"+name);
                },
                (msg)->{
//            對字符串進行分割,取出性別
            String sex = msg.split(",")[1];
                    System.out.println("性別:"+sex);
                });

    }
}

 

 

Predicate接口

 

/**
 * java.util.function.Predicate<T>接口
 * 作用:對某種數據類型的數據進行判斷,結果返回一個boolean值
 *     接口有一個抽象方法:
 *     boolean test(T t):用來對指定數據類型數據進行判斷
 *
 *
 */

 

public class PredicateTest {
    //定義方法,參數傳遞一個字符串,Predicate接口
    public static boolean checkString(String s, Predicate<String> pred){
        return  pred.test(s);
    }

    public static void main(String[] args) {
        String  s ="ABCD";
        System.out.println(checkString(s,(ss)->{
            if (ss.length()>5){
                return true;
            }else {
                return false;
            }
        })
        );
    }
}

 

接口中的默認方法,and  or negate:

/**
 * 判斷一個字符串長度大於5 且字符串是否包含a
 */
public class PredicateT {
    public static boolean checkString(String s, Predicate<String> p1, Predicate<String> p2){
//        return  p1.test(s) && p2.test(s);
        return p1.and(p2).test(s);//等價於上面
//        return p1.or(p2).test(s);////        return p1.negate().test(s)//negate非
    }

    public static void main(String[] args) {
        String s = "anbced";
        boolean re =checkString(s,
                (ss)->{
            return ss.length()>5;
                },
                (ss)->{
            return ss.contains("a");
                });
        System.out.println(re);
    }
}

 

練習:

/**
 * 將數組里面的名字等於4位的且是女生的用arraylist保存起來
 */
public class PredicateTest2 {
    private static void xuanze(String[] arr, Predicate<String> pre1, Predicate<String> pre2){
        List<String> stringList =new ArrayList<>();
        for (String s : arr) {
            if(pre1.and(pre2).test(s)){
                stringList.add(s);
            }
        }
        System.out.println(stringList);
    }

    public static void main(String[] args) {
        String[] arr = {"1111,N","222,M","44444","N"};
        xuanze(arr,
                (s)->{
                 return  s.split(",")[0].length()==4;
                },
                (s)->{
                return s.split(",")[1].equals("N");
                });
    }
}

 

Function接口

/**
 * java.util.function.Function<T,R>接口用來根據一個類型的數據得到另一個類型的數據
 *                          前者位前置條件,后者位后置條件
 *  接口里面的抽象方法: R apply<T t>,根據類型T參數獲取類型R參數的結果。
 *          了例如:String類型轉換位Integer類型
 */

 

public class FunctionDemo {
    public static void exchange(String s, Function<String,Integer> function){
        Integer i = function.apply(s);
//        int i1 = function.apply(s);自動拆箱,將Integer類型自動拆成Int類型
        System.out.println(i);
    }

    public static void main(String[] args) {
        String si = "123";
        exchange(si,(ss)->{
           return Integer.parseInt(ss);
        });
//優化
//        exchange(si,(ss)-> Integer.parseInt(ss)
//        );
    }
}

 

默認方法andThen

 

/**
 * Function接口的默認方法andThen:用來進行組合操作
 * 需求:String類型的123 轉換為Integer類型,把轉換結果加10
 * 把增加之后的Integer類型的數據轉換為String類型
 *
 */
public class FunctionDemo2_andThen {

    private static void change(String s,Function<String,Integer> f1,Function<Integer,String> f2){
       String ss =  f1.andThen(f2).apply(s);
        System.out.println(ss);
    }

    public static void main(String[] args) {
        String s = "123";
        change(s,
                (s1)->{
            //把字符串轉整數
                    return Integer.parseInt(s1)+10;
                },
                (i)->{
            return i+"";
                });
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM