函数式接口
函数式接口
1.1概念:有且仅有一个抽象方法的接口。
函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可 以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。
附注:语法糖-->指使用更加方便,但是原理不变的代码语法。例如遍历集合使用的for-each语法,底层实现原理就是迭代器,这便是“语法糖”。从应用层面来讲,Java中的Lambda可以被当做是匿名内部 类的“语法糖”。
1.2语法
public interface MyFunctionalInterface { void myMethod(); } 确保接口最终有且只有一个抽象方法。
1.3@FunctionalInterface注解 since(JDK8)
注意:即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。
1.4自定义函数式接口
对于函数式接口来说,典型使用场景就是作为方法的参数(或者作为返回值)。
函数式编程
2.1Lambda的延迟执行
有些场景的代码执行后,结果不一定会被使用,从而造成性能的浪费。而LAMbda的延迟执行,可作为解决方案。
性能浪费的日志案例:
public static void logger(int level,String msg){ if(level=1){ System.out.println(msg); } } public static void main(String args[]){ String msgA = "Hello"; String msgB = "World"; String msgC = "Java"; //当执行到这个方法的时候,一定会进行字符串的拼接,不管level够不够 logger(1,msgA+msgB+msgC); }
使用Lambda表达式进行优化:
@FunctionalInterface public interface MessageBuilder{ String buildMessage(); } public static void logger(int level,MessageBuilder builder){ if(level=1){ System.out.println(builder.buildMessage()); } } pubblic static void main(){ String msgA = "Hello"; String msgB = "World"; String msgC = "Java"; //只是在此处创建接口抽象方法的方法体,真正执行是在level符合的时候 //因此不会造成资源的浪费 logger(1,()->msgA+msgB+msgC); }
验证:
//延迟执行的验证 pubblic static void main(){ String msgA = "Hello"; String msgB = "World"; String msgC = "Java"; logger(1,()->{ System.out.println("执行了....."); return msgA+msgB+msgC; }); }
2.2接口(函数式接口)作为参数和返回值
Java中的Lambda表达式可以当做匿名内部类的替代品。如果方法的参数是一个接口,那么可以用lambda表达式替换。使用Lambda表达式作为参数,实际上就是使用函数式接口做为参数。因为:Lambda表达式就相当于该接口的一个匿名实现类(多态),也算是“子类”。
//函数式接口作为参数==>lambda表达式作为参数 public static void startThread(Runnable task){ new Thread(task).start(); } public static void main(String[] args){ startThread(()->System.out.println("开始线程....")) }
//lambda表达式作为返回值 public static Comparator<String> newComparator(){ return (a,b)->b.length()-a.length(); //==》此处可以根据返回值推导类型 } public static void main(String[] args){ String[] arr ={aa,abc,abcd}; System.out.println(Arrays.toString(arr)); Arrays.sort(arr,newComparator); System.out.println(Arrays.toString(arr)); }
**本质上:Lambda表达就是接口的一个匿名的实现类。不管传入的参数或是返回值。
常见的函数式接口
JDK提供了大量常用的函数式接口以丰富Lambda的典型使用场景,它们主要在 java.util.function 包中被提供。
3.1Supplier接口
java.util.function.Supplier<T> 接口仅包含一个无参的方法:T get() 。用来获取一个泛型参数指定类型的对象数据。
public static void demoSupplier(Suppplier sup){ retun sup.get(); } public static void main(String[] args){ String msgA = "Hello"; String msgB = "World"; //msgA+msgB相当于重写了接口中的抽象方法 //整个Lambda表达式相当于一个该接口匿名实现类的对象 System.out.println(demoSupplier(()->msgA+msgB)
); }
//求数组元素的最大值 public static void getMax(Suppplier sup){ retun sup.get(); } public static void main(String[] args){ int[] arr = {15,2,55,20,3,0,36}; int maxNum = getMax(()->{ int max = arr[0]; for(int i:arr){ if(i>max){ max = i; } } retun max; }); System.out.println(maxNum); }
3.2Comsumer接口
java.util.function.Consumer<T> 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据。其数据类型由泛型决定。
抽象方法:accept
含抽象方法 void accept(T t) ,意为消费一个指定泛型的数据。
public class DemoConsumer{ public static consume(Consumer con){ con.accept("hello"); } public static void main(String[] args){ consume((s)-> s.toUpperCase()); } }
默认方法:addThen
源码:
default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; }
附注:java.util.Objects 的 requireNonNull 静态方法将会在参数为null时主动抛出NullPointerException 异常。这省去了重复编写if语句和抛出空指针异常的麻烦。
原理:
return (T t)->{accept(t);after.accept()}新的一个==》new Consumer<T t>{ @override accept(T t){accept(t);after.accept(t)}};
==》如果过再加上一个andThen newConsumer<T t>{@override accept(T t){ accept(t); three.accept(t) }}
此时:accept(t);-->相当于accept(T t){accept(t);after.accept(t)} ==》嵌套的关系
public class DemoConsumer2{ public static consume(String info,Consumer con1){ con1.andThen(con2).add(con3).accept(info); } public static void main(String[] args){ consume(s,s->System.out.println(s.toUpperCase()), s->System.out.println(s.toLowerCase()),s->System.out.println(s.toUpperCase())); } }
案例:
public calss DemoConsumer3{ public getInfo(String[] arr,Consumer con1,Consumer con2){ for(String str:arr){ con1.andThen(con2).accept(str); } } public static void main(String[] args){ String[] arr = {"迪丽热巴,女","古力娜扎,女","马尔扎哈,男"}; getInfo(arr,s->System.out.println("姓名:"+s.split(",")[0]), s->System.out.println("性别:"+s.split(",")[1])) } }
3.3Predicate接口
java.util.function.Predicate<T> 需要对某种类型的数据进行判断,从而得到一个boolean值结果。
抽象方法:boolean test(T t) 。用于条件判断的场景。
默认方法:and or negate
执行了test方法之后,对结果boolean值进行“!”取反而已。一定要在 test 方法调用之前 调用 negate 方法,正如 and 和 or 方法一样。
3.4Function接口
java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件, 后者称为后置条件。
抽象方法:apply
默认方法:andThen

浙公网安备 33010602011771号