是什么??
有且只有一個抽象方法的接口
場景:
適用於函數式編程場景(使用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+""; }); } }
