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+""; }); } }