一、函数式接口
函数式接口在Java中是指:有且仅有一个抽象方法的接口,接口中可以包含其他的方法(默认,静态,私有)
函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。
只有确保接口中有且仅有一个抽象方法,java中的Lambda才能顺利地进行推导。
语法糖:
“语法糖”是指使用更加方便,但是原理不变的代码语法。例如在遍历集合时使用的for-each语法,
其实底层的实现原理仍然是迭代器,这便是“语法糖”。从应用层面来讲,java中的Lambda可以
被当做是匿名内部类的 “语法糖”,但是二者在原理上是不同的。
注解:
@Override注解
检查方法是否为重写的方法
是:编译成功
否:编译失败
@FunctionalInterface注解
作用:可以检查接口是否是一个函数式接口
是:编译成功
否:编译失败(接口中没有抽象方法或者接口中的抽象方法多于1个)
二、Lambda表达式
1.Lambda的特点:
延迟加载,即满足条件才会执行,不满足条件就不执行。
2.Lambda的使用前提:
必须存在函数式接口
3.Lambda表达式作为方法的参数
如果抛开实现原理不说,java中的Lambda表达式可以被当做是匿名内部类的替代品。
如果方法的参数是一个函数式接口类型,那么就可以使用Lambda表达式进行替代。
使用Lambda表达式作为方法参数,其实就是使用函数式接口作为方法参数。
例如java.lang.Runnable接口就是一个函数式接口,
假设有一个startThread方法使用该接口作为参数,那么就可以使用Lambda进行传参。
这种情况其实和Thread类的构造方法参数为Runnable没有本质区别。
使用Lambda表达式作为参数的前提:
方法的参数是函数式接口
4.Lambda表达式作为方法的返回值
如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式。
当需要通过一个方法来获取一个java.util.Comparator接口类型的对象作为排序器时,就可以调用该方法获取,代码如下:
public static Comparator<String> getComparator(){
//方法的返回值类型是一个接口,那么我们可以返回这个接口的匿名内部类
// return new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// //排序规则:按照字符串的降序排序
// return o2.length() - o1.length();
// }
// };
//方法的返回值类型是一个函数式接口,所以可以返回一个Lambda表达式
// return (String o1, String o2)->{
// //排序规则:按照字符串的降序排序
// return o2.length() - o1.length();
// };
//优化Lambda
//参数的数据类型可以省略:String
//方法体只有一条语句,return可以省略,{}和;可以省略
return (o1, o2)->o2.length() - o1.length();
}
三、常用的函数式接口
1.Supplier接口
java.util.function.Supplier接口仅包含一个无参的方法:T.get()。
用来获取一个泛型参数指定类型的对象数据。
Supplier接口被称为【生产型接口】,指定接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据。
2.Consumer接口
2.1 java.util.function.Consumer
该接口正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定。
Consumer接口中包含抽象方法void accept(T t),意为消费一个指定泛型的数据。
Consumer接口是一个消费型接口,泛型指定什么类型,就可以使用accept方法消费什么类型的数据。至于具体怎么消费(使用),需要自定义(例如输出,计算等)
2.2 Consumer接口的默认方法andThen
作用:需要两个Consumer接口,可以把两个Consumer接口组合到一起,再对数据进行消费
例如:
Consumer<String> con1
Consumer<String> con2
String s = "java";
con1.accept(s);
con2.accept(s);
使用andThen进行优化:连接两个Consumer接口,再对数据进行消费
//谁写在前面,谁就先进行消费
//con1先进行消费,然后是con2
con1.andThen(con2).accept(s);
3.Predicate接口
作用:对某种数据类型的数据进行判断,结果返回一个boolean值
Predicate接口中包含一个抽象方法:
boolean test<T t>:用来对指定数据类型数据进行判断的方法
结果:
符合条件:返回true
不符合条件:返回false
逻辑表达式:可以连接多个判断的条件
&&:与运算符,有false则false
||:或运算符,有true则true
!:非运算符:true则false;false则true
3.1 and方法
Predicate接口中有一个and方法,表示并且关系,也可以用于连接两个判断条件
default Predicate<T> and(Predicate<? super T> other){
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
可见and方法内部的两个判断条件,也是使用&&运算符连接起来的
3.2 or方法
Predicate接口中有一个or方法,表示或者关系,也可以用于连接两个判断条件
default Predicate<T> or(Predicate<? super T> other){
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
可见or方法内部的两个判断条件,也是使用||运算符连接起来的
3.3 negate方法
Predicate接口中有一个方法negate,表示取反的意思
default Predicate<T> negate(){
return (t) -> !test(t);
}
4.Function接口
1.Function接口及其抽象方法
java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据,
前者称为前置条件,后者称为后置条件。即该接口用于【类型转换】。
Function接口中最主要的抽象方法为:R apply(T t),根据类型T的参数获取类型R的结果。 例如:将String类型转换为Integer类型。
2.Function接口中的默认方法andThen
作用:进行组合操作
需求:
把String类型的“123”转换为Integer,把转换后的结果加10
把增加之后的Integer类型的数据,转换为String类型
分析:
转换了两次:
第一次是把String类型转换为Integer类型
可以使用Function<String Integer> fun1
Integer i = fun1.apply("123") + 10;
第二次是把Integer类型转化为String类型
可以使用Function<Integer, String> fun2
String s = fun2.apply(i);
可以使用andThen方法,把两次转换组合在一起使用
String s = fun1.andThen(fun2).apply("123");
fun1先调用apply方法,把字符串转换为Integer
fun2再调用apply方法,把Integer转换为字符串