函数式接口
函数式接口在Java中是指:有且仅有一个抽象方法的接口。
函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可 以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。
@FunctionalInterface注解
与 @Override 注解的作用类似,Java 8中专门为函数式接口引入了的新的注解
该注解可用于一个接口的定义上
1 2 3 4 5 6 7 8 9 10
| @FunctionalInterface public interface MyFunctionalInterface { void myMethod(); }
class MyFunctionalInterfaceImpl implements MyFunctionalInterface { @Override public void method() { } }
|
一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。需要注意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。
函数式接口的使用
定义一个方法,参数使用函数式接口MyFunctionalInterface
使用此方法时,需要重写接口的method方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class Demo { public static void show(MyFunctionalInterface myInter){ myInter.method(); }
public static void main(String[] args) { show(new MyFunctionalInterfaceImpl());
show(new MyFunctionalInterface() { @Override public void method() { System.out.println("使用匿名内部类重写接口中的抽象方法"); } });
show(()->{ System.out.println("使用Lambda表达式重写接口中的抽象方法"); });
show(()-> System.out.println("使用Lambda表达式重写接口中的抽象方法")); }
}
|
函数式编程
日志案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class Demo01Logger { public static void showLog(int level, String message){ if(level==1){ System.out.println(message); } }
public static void main(String[] args) { String msg1 = "Hello"; String msg2 = "World"; String msg3 = "Java"; showLog(2, msg1 + msg2 + msg3); } }
|
发现以上代码存在的一些性能浪费的问题,调用showLog方法,传递的第二个参数是一个拼接后的字符串,先把字符串拼接好,然后在调用showLog方法,showLog方法中如果传递的日志等级不是1级,那么就不会是如此拼接后的字符串,所以感觉字符串就白拼接了,存在了浪费。
Lambda的延迟执行的好处
有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而Lambda表达式是延迟执行的,这正好可以作为解决方案,提升性能。
参数为函数式接口
使用Lambda表达式作为参数传递,仅仅是把参数传递到showLog方法中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| @FunctionalInterface public interface MessageBuilder { public abstract String builderMessage(); }
class Demo02Lambda {
public static void showLog(int level, MessageBuilder mb){ if(level == 1){ System.out.println(mb.builderMessage()); } }
public static void main(String[] args) { String msg1 = "Hello"; String msg2 = "World"; String msg3 = "Java";
showLog(1,()->{ System.out.println("不满足条件不执行"); return msg1+msg2+msg3; });
} }
|
方法返回函数式接口
如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式。
当需要通过一个方法来获取一个java.util.Comparator接口类型的对象作为排序器时,就可以调该方法获取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| public class Demo02Comparator {
public static Comparator<String> getComparator(){
return (o1, o2)->o2.length()-o1.length(); }
public static void main(String[] args) { String[] arr = {"aaa","b","cccccc","dddddddddddd"}; 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表达式需要“对外提供”一个符合泛型类型的对象数据。
Supplier< T>接口被称之为生产型接口,指定接口的泛型是什么类型,那么接口中的get方法就会生产(return)什么类型的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class Demo01Supplier { public static String getString(Supplier<String> sup){ return sup.get(); }
public static void main(String[] args) { String s = getString(()->{ return "胡歌"; }); System.out.println(s);
String s2 = getString(()->"胡歌"); System.out.println(s2); } }
|
练习:求数组元素最大值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class Demo02Test { public static int getMax(Supplier<Integer> sup){ return sup.get(); }
public static void main(String[] args) { int[] arr = {100,0,-50,880,99,33,-30}; int maxValue = getMax(()->{ int max = arr[0]; for (int i : arr) { if(i>max){ max = i; } } return max; }); System.out.println("数组中元素的最大值是:"+maxValue); } }
|
Consumer接口
java.util.function.Consumer< T> :正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定。 接口中包含抽象方法
void accept(T t) :意为消费一个指定泛型的数据。泛型是什么数据类型,就传入什么类型的参数
使用消费者作为方法的参数,一般要定义另一个参数,其类型等于消费者接口的泛型,或者该泛型的数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class Demo01Consumer {
public static void method(String name, Consumer<String> con){ con.accept(name); }
public static void main(String[] args) { method("赵丽颖",(String name)->{ String reName = new StringBuffer(name).reverse().toString(); System.out.println(reName); }); } }
|
default Consumer andThen(Consumer super < T> after)
如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费数据的时候,首先做一个操作, 然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的默认方法方法andThen
1 2 3 4 5 6 7
| default Consumer andThen(Consumer super <T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; }
|
常用写法
Consumer1< T>.andThen(Consumer2< T>).accept(T):谁写前边谁先消费
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class Demo02AndThen { public static void method(String s, Consumer<String> con1 ,Consumer<String> con2){ con1.andThen(con2).accept(s); }
public static void main(String[] args) { method("Hello", (t)->{ System.out.println(t.toUpperCase()); }, (t)->{ System.out.println(t.toLowerCase()); }); } }
|
练习:格式化打印信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Demo03Test { public static void main(String[] args) { String[] array = {"迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男"}; printInfo(s-> System.out.print("姓名:" + s.split(",")[0]), s-> System.out.println("。性别:" + s.split(",")[1] + "。"), array); }
private static void printInfo(Consumer<String> one, Consumer<String> two, String[] array) { for (String info : array) { one.andThen(two).accept(info); } } }
|
Predicate接口
java.util.function.Predicate< T> :有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果,这时可以使用接口。Predicate 接口中包含一个抽象方法
boolean test(T t) :用于对输入进行条件判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class Demo01Predicate {
public static boolean checkString(String s, Predicate<String> pre) { return pre.test(s); }
public static void main(String[] args) { String s = "abcdef";
boolean b = checkString(s, str -> str.length() > 5); System.out.println(b); } }
|
default Predicate< T> and(Predicate<? super T> other)
Predicate接口中有一个方法and,表示并且关系,也可以用于连接两个判断条件
1 2 3 4
| default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> this.test(t) && other.test(t); }
|
方法内部的两个判断条件,也是使用&&运算符连接起来的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class Demo02Predicate_and {
public static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2){ return pre1.and(pre2).test(s); }
public static void main(String[] args) { String s = "abcdef"; boolean b = checkString(s, str->{ return str.length()>5; }, str->{ return str.contains("a"); }); System.out.println(b); } }
|
default Predicate< T> or(Predicate<? super T> other)
表示或者关系,也可以用于连接两个判断条件
1 2 3 4
| default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); }
|
default Predicate< T> negate()
表示取反,返回的是一个Predicate接口
1 2 3
| default Predicate<T> negate() { return (t) -> !test(t); }
|
返回当前 Predicate
对象的逻辑非(negation),即返回一个新的 Predicate
对象,其判断条件与原始 Predicate
对象相反。
1 2 3 4 5 6 7 8 9 10 11 12 13
| Predicate<Integer> isPositive = num -> num > 0;
System.out.println(isPositive.test(5)); System.out.println(isPositive.test(-3));
Predicate<Integer> isNotPositive = isPositive.negate();
System.out.println(isNotPositive.test(5)); System.out.println(isNotPositive.test(-3));
|
Function接口
java.util.function.Function<T,R> :用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件,Function 接口中最主要的抽象方法为:
R apply(T t) ,根据类型T的参数获取类型R的结果
使用的场景:将 String 类型转换为 Integer 类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class Demo01Function {
public static void change(String s, Function<String,Integer> fun){ int in = fun.apply(s); System.out.println(in); }
public static void main(String[] args) { String s = "1234"; change(s,(String str)->{ return Integer.parseInt(str); }); change(s, str->Integer.parseInt(str)); } }
|
default < V> Function<T, V> andThen(Function<? super R, ? extends V> after)
用于将当前 Function
对象的输出作为参数传递给另一个 Function
对象,并返回一个新的 Function
对象,实现了这两个函数的串联操作。
1 2 3 4
| default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); }
|
该方法同样用于“先做什么,再做什么”的场景,和 Consumer 中的 andThen 差不多
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class Demo02Function_andThen {
public static void change(String s, Function<String,Integer> fun1,Function<Integer,String> fun2){ String ss = fun1.andThen(fun2).apply(s); System.out.println(ss); }
public static void main(String[] args) { String s = "123"; change(s,(String str)->{ return Integer.parseInt(str) + 10; },(Integer i)->{ return i+""; });
change(s, str->Integer.parseInt(str) + 10, i-> i + ""); } }
|